diff options
author | sanine <sanine.not@pm.me> | 2024-07-05 16:38:44 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2024-07-05 16:38:44 -0500 |
commit | b4227d3c206aa122b9f4e562760a568a99dd623e (patch) | |
tree | a2feda9ba9407e754ef9dfbb9069602f97f99292 /mathjs/mathjs-expression-parser.js |
basic trajectory plotting
Diffstat (limited to 'mathjs/mathjs-expression-parser.js')
-rw-r--r-- | mathjs/mathjs-expression-parser.js | 13005 |
1 files changed, 13005 insertions, 0 deletions
diff --git a/mathjs/mathjs-expression-parser.js b/mathjs/mathjs-expression-parser.js new file mode 100644 index 0000000..4d521b9 --- /dev/null +++ b/mathjs/mathjs-expression-parser.js @@ -0,0 +1,13005 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.math = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ +// Load the math.js core +var core = require('mathjs/core'); + +// Create a new, empty math.js instance +// It will only contain methods `import` and `config` +var math = core.create(); + +math.import(require('mathjs/lib/expression/function/parse')); +math.import(require('mathjs/lib/expression/function/compile')); +math.import(require('mathjs/lib/expression/function/eval')); + +math.import(require('mathjs/lib/function/string/format')); + +// create simple functions for all operators +math.import({ + // arithmetic + add: function (a, b) { return a + b }, + subtract: function (a, b) { return a - b }, + multiply: function (a, b) { return a * b }, + divide: function (a, b) { return a / b }, + mod: function (a, b) { return a % b }, + unaryPlus: function (a) { return a }, + unaryMinus: function (a) { return -a }, + + // bitwise + bitOr: function (a, b) { return a | b }, + bitXor: function (a, b) { return a ^ b }, + bitAnd: function (a, b) { return a & b }, + bitNot: function (a) { return ~a }, + leftShift: function (a, b) { return a << b }, + rightArithShift: function (a, b) { return a >> b }, + rightLogShift: function (a, b) { return a >>> b }, + + // logical + or: function (a, b) { return !!(a || b) }, + xor: function (a, b) { return !!a !== !!b }, + and: function (a, b) { return !!(a && b) }, + not: function (a) { return !a }, + + // relational + equal: function (a, b) { return a == b }, + unequal: function (a, b) { return a != b }, + smaller: function (a, b) { return a < b }, + larger: function (a, b) { return a > b }, + smallerEq: function (a, b) { return a <= b }, + largerEq: function (a, b) { return a >= b }, + + // matrix + // matrix: function (a) { return a }, + matrix: function () { + throw new Error('Matrices not supported') + }, + index: function () { + // TODO: create a simple index function + throw new Error('Matrix indexes not supported') + }, + + // add pi and e as lowercase + pi: Math.PI, + e: Math.E, + 'true': true, + 'false': false, + 'null': null +}) + +// import everything from Math (like trigonometric functions) +var allFromMath = {}; +Object.getOwnPropertyNames(Math).forEach(function (name) { + // filter out stuff like Firefox's "toSource" method. + if (!Object.prototype.hasOwnProperty(name)) { + allFromMath[name] = Math[name]; + } +}); +math.import(allFromMath); + +module.exports = math; + +},{"mathjs/core":3,"mathjs/lib/expression/function/compile":11,"mathjs/lib/expression/function/eval":12,"mathjs/lib/expression/function/parse":13,"mathjs/lib/function/string/format":37}],2:[function(require,module,exports){ +"use strict"; + +// Map the characters to escape to their escaped values. The list is derived +// from http://www.cespedes.org/blog/85/how-to-escape-latex-special-characters + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var defaultEscapes = { + "{": "\\{", + "}": "\\}", + "\\": "\\textbackslash{}", + "#": "\\#", + $: "\\$", + "%": "\\%", + "&": "\\&", + "^": "\\textasciicircum{}", + _: "\\_", + "~": "\\textasciitilde{}" +}; +var formatEscapes = { + "\u2013": "\\--", + "\u2014": "\\---", + " ": "~", + "\t": "\\qquad{}", + "\r\n": "\\newline{}", + "\n": "\\newline{}" +}; + +var defaultEscapeMapFn = function defaultEscapeMapFn(defaultEscapes, formatEscapes) { + return _extends({}, defaultEscapes, formatEscapes); +}; + +/** + * Escape a string to be used in LaTeX documents. + * @param {string} str the string to be escaped. + * @param {boolean} params.preserveFormatting whether formatting escapes should + * be performed (default: false). + * @param {function} params.escapeMapFn the function to modify the escape maps. + * @return {string} the escaped string, ready to be used in LaTeX. + */ +module.exports = function (str) { + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$preserveFormatti = _ref.preserveFormatting, + preserveFormatting = _ref$preserveFormatti === undefined ? false : _ref$preserveFormatti, + _ref$escapeMapFn = _ref.escapeMapFn, + escapeMapFn = _ref$escapeMapFn === undefined ? defaultEscapeMapFn : _ref$escapeMapFn; + + var runningStr = String(str); + var result = ""; + + var escapes = escapeMapFn(_extends({}, defaultEscapes), preserveFormatting ? _extends({}, formatEscapes) : {}); + var escapeKeys = Object.keys(escapes); // as it is reused later on + + // Algorithm: Go through the string character by character, if it matches + // with one of the special characters then we'll replace it with the escaped + // version. + + var _loop = function _loop() { + var specialCharFound = false; + escapeKeys.forEach(function (key, index) { + if (specialCharFound) { + return; + } + if (runningStr.length >= key.length && runningStr.slice(0, key.length) === key) { + result += escapes[escapeKeys[index]]; + runningStr = runningStr.slice(key.length, runningStr.length); + specialCharFound = true; + } + }); + if (!specialCharFound) { + result += runningStr.slice(0, 1); + runningStr = runningStr.slice(1, runningStr.length); + } + }; + + while (runningStr) { + _loop(); + } + return result; +}; +},{}],3:[function(require,module,exports){ +module.exports = require('./lib/core/core') + +},{"./lib/core/core":4}],4:[function(require,module,exports){ +'use strict'; + +require('./../utils/polyfills'); + +var isFactory = require('./../utils/object').isFactory; + +var typedFactory = require('./typed'); + +var emitter = require('./../utils/emitter'); + +var importFactory = require('./function/import'); + +var configFactory = require('./function/config'); +/** + * Math.js core. Creates a new, empty math.js instance + * @param {Object} [options] Available options: + * {number} epsilon + * Minimum relative difference between two + * compared values, used by all comparison functions. + * {string} matrix + * A string 'Matrix' (default) or 'Array'. + * {string} number + * A string 'number' (default), 'BigNumber', or 'Fraction' + * {number} precision + * The number of significant digits for BigNumbers. + * Not applicable for Numbers. + * {boolean} predictable + * Predictable output type of functions. When true, + * output type depends only on the input types. When + * false (default), output type can vary depending + * on input values. For example `math.sqrt(-4)` + * returns `complex('2i')` when predictable is false, and + * returns `NaN` when true. + * {string} randomSeed + * Random seed for seeded pseudo random number generator. + * Set to null to randomly seed. + * @returns {Object} Returns a bare-bone math.js instance containing + * functions: + * - `import` to add new functions + * - `config` to change configuration + * - `on`, `off`, `once`, `emit` for events + */ + + +exports.create = function create(options) { + // 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.'); + } // cached factories and instances + + + var factories = []; + var instances = []; // create a namespace for the mathjs instance, and attach emitter functions + + var math = emitter.mixin({}); + math.type = {}; + math.expression = { + transform: {}, + mathWithTransform: {} // create a new typed instance + + }; + math.typed = typedFactory.create(math.type); // create configuration options. These are private + + var _config = { + // minimum relative difference between two compared values, + // used by all comparison functions + epsilon: 1e-12, + // type of default matrix output. Choose 'matrix' (default) or 'array' + matrix: 'Matrix', + // type of default number output. Choose 'number' (default) 'BigNumber', or 'Fraction + number: 'number', + // number of significant digits in BigNumbers + precision: 64, + // predictable output type of functions. When true, output type depends only + // on the input types. When false (default), output type can vary depending + // on input values. For example `math.sqrt(-4)` returns `complex('2i')` when + // predictable is false, and returns `NaN` when true. + predictable: false, + // random seed for seeded pseudo random number generation + // null = randomly seed + randomSeed: null + /** + * Load a function or data type from a factory. + * If the function or data type already exists, the existing instance is + * returned. + * @param {{type: string, name: string, factory: Function}} factory + * @returns {*} + */ + + }; + + function load(factory) { + if (!isFactory(factory)) { + throw new Error('Factory object with properties `type`, `name`, and `factory` expected'); + } + + var index = factories.indexOf(factory); + var instance; + + if (index === -1) { + // doesn't yet exist + if (factory.math === true) { + // pass with math namespace + instance = factory.factory(math.type, _config, load, math.typed, math); + } else { + instance = factory.factory(math.type, _config, load, math.typed); + } // append to the cache + + + factories.push(factory); + instances.push(instance); + } else { + // already existing function, return the cached instance + instance = instances[index]; + } + + return instance; + } // load the import and config functions + + + math['import'] = load(importFactory); + math['config'] = load(configFactory); + math.expression.mathWithTransform['config'] = math['config']; // apply options + + if (options) { + math.config(options); + } + + return math; +}; +},{"./../utils/emitter":52,"./../utils/object":55,"./../utils/polyfills":56,"./function/config":5,"./function/import":6,"./typed":7}],5:[function(require,module,exports){ +'use strict'; + +var object = require('../../utils/object'); + +function factory(type, config, load, typed, math) { + var MATRIX = ['Matrix', 'Array']; // valid values for option matrix + + var NUMBER = ['number', 'BigNumber', 'Fraction']; // valid values for option number + + /** + * Set configuration options for math.js, and get current options. + * Will emit a 'config' event, with arguments (curr, prev, changes). + * + * Syntax: + * + * math.config(config: Object): Object + * + * Examples: + * + * math.config().number // outputs 'number' + * math.eval('0.4') // outputs number 0.4 + * math.config({number: 'Fraction'}) + * math.eval('0.4') // outputs Fraction 2/5 + * + * @param {Object} [options] Available options: + * {number} epsilon + * Minimum relative difference between two + * compared values, used by all comparison functions. + * {string} matrix + * A string 'Matrix' (default) or 'Array'. + * {string} number + * A string 'number' (default), 'BigNumber', or 'Fraction' + * {number} precision + * The number of significant digits for BigNumbers. + * Not applicable for Numbers. + * {string} parenthesis + * How to display parentheses in LaTeX and string + * output. + * {string} randomSeed + * Random seed for seeded pseudo random number generator. + * Set to null to randomly seed. + * @return {Object} Returns the current configuration + */ + + function _config(options) { + if (options) { + var prev = object.map(config, object.clone); // validate some of the options + + validateOption(options, 'matrix', MATRIX); + validateOption(options, 'number', NUMBER); // merge options + + object.deepExtend(config, options); + var curr = object.map(config, object.clone); + var changes = object.map(options, object.clone); // emit 'config' event + + math.emit('config', curr, prev, changes); + return curr; + } else { + return object.map(config, object.clone); + } + } // attach the valid options to the function so they can be extended + + + _config.MATRIX = MATRIX; + _config.NUMBER = NUMBER; + return _config; +} +/** + * Test whether an Array contains a specific item. + * @param {Array.<string>} array + * @param {string} item + * @return {boolean} + */ + + +function contains(array, item) { + return array.indexOf(item) !== -1; +} +/** + * Find a string in an array. Case insensitive search + * @param {Array.<string>} array + * @param {string} item + * @return {number} Returns the index when found. Returns -1 when not found + */ + + +function findIndex(array, item) { + return array.map(function (i) { + return i.toLowerCase(); + }).indexOf(item.toLowerCase()); +} +/** + * Validate an option + * @param {Object} options Object with options + * @param {string} name Name of the option to validate + * @param {Array.<string>} values Array with valid values for this option + */ + + +function validateOption(options, name, values) { + if (options[name] !== undefined && !contains(values, options[name])) { + var index = findIndex(values, options[name]); + + if (index !== -1) { + // right value, wrong casing + // TODO: lower case values are deprecated since v3, remove this warning some day. + console.warn('Warning: Wrong casing for configuration option "' + name + '", should be "' + values[index] + '" instead of "' + options[name] + '".'); + options[name] = values[index]; // change the option to the right casing + } else { + // unknown value + console.warn('Warning: Unknown value "' + options[name] + '" for configuration option "' + name + '". Available options: ' + values.map(JSON.stringify).join(', ') + '.'); + } + } +} + +exports.name = 'config'; +exports.math = true; // request the math namespace as fifth argument + +exports.factory = factory; +},{"../../utils/object":55}],6:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var lazy = require('../../utils/object').lazy; + +var isFactory = require('../../utils/object').isFactory; + +var traverse = require('../../utils/object').traverse; + +var ArgumentsError = require('../../error/ArgumentsError'); + +function factory(type, config, load, typed, math) { + /** + * Import functions from an object or a module + * + * Syntax: + * + * math.import(object) + * math.import(object, options) + * + * Where: + * + * - `object: Object` + * An object with functions to be imported. + * - `options: Object` An object with import options. Available options: + * - `override: boolean` + * If true, existing functions will be overwritten. False by default. + * - `silent: boolean` + * If true, the function will not throw errors on duplicates or invalid + * types. False by default. + * - `wrap: boolean` + * If true, the functions will be wrapped in a wrapper function + * which converts data types like Matrix to primitive data types like Array. + * The wrapper is needed when extending math.js with libraries which do not + * support these data type. False by default. + * + * Examples: + * + * // define new functions and variables + * math.import({ + * myvalue: 42, + * hello: function (name) { + * return 'hello, ' + name + '!' + * } + * }) + * + * // use the imported function and variable + * math.myvalue * 2 // 84 + * math.hello('user') // 'hello, user!' + * + * // import the npm module 'numbers' + * // (must be installed first with `npm install numbers`) + * math.import(require('numbers'), {wrap: true}) + * + * math.fibonacci(7) // returns 13 + * + * @param {Object | Array} object Object with functions to be imported. + * @param {Object} [options] Import options. + */ + function mathImport(object, options) { + var num = arguments.length; + + if (num !== 1 && num !== 2) { + throw new ArgumentsError('import', num, 1, 2); + } + + if (!options) { + options = {}; + } // TODO: allow a typed-function with name too + + + if (isFactory(object)) { + _importFactory(object, options); + } else if (Array.isArray(object)) { + object.forEach(function (entry) { + mathImport(entry, options); + }); + } else if (_typeof(object) === 'object') { + // a map with functions + for (var name in object) { + if (object.hasOwnProperty(name)) { + var value = object[name]; + + if (isSupportedType(value)) { + _import(name, value, options); + } else if (isFactory(object)) { + _importFactory(object, options); + } else { + mathImport(value, options); + } + } + } + } else { + if (!options.silent) { + throw new TypeError('Factory, Object, or Array expected'); + } + } + } + /** + * Add a property to the math namespace and create a chain proxy for it. + * @param {string} name + * @param {*} value + * @param {Object} options See import for a description of the options + * @private + */ + + + function _import(name, value, options) { + // TODO: refactor this function, it's to complicated and contains duplicate code + if (options.wrap && typeof value === 'function') { + // create a wrapper around the function + value = _wrap(value); + } + + if (isTypedFunction(math[name]) && isTypedFunction(value)) { + if (options.override) { + // give the typed function the right name + value = typed(name, value.signatures); + } else { + // merge the existing and typed function + value = typed(math[name], value); + } + + math[name] = value; + + _importTransform(name, value); + + math.emit('import', name, function resolver() { + return value; + }); + return; + } + + if (math[name] === undefined || options.override) { + math[name] = value; + + _importTransform(name, value); + + math.emit('import', name, function resolver() { + return value; + }); + return; + } + + if (!options.silent) { + throw new Error('Cannot import "' + name + '": already exists'); + } + } + + function _importTransform(name, value) { + if (value && typeof value.transform === 'function') { + math.expression.transform[name] = value.transform; + + if (allowedInExpressions(name)) { + math.expression.mathWithTransform[name] = value.transform; + } + } else { + // remove existing transform + delete math.expression.transform[name]; + + if (allowedInExpressions(name)) { + math.expression.mathWithTransform[name] = value; + } + } + } + + function _deleteTransform(name) { + delete math.expression.transform[name]; + + if (allowedInExpressions(name)) { + math.expression.mathWithTransform[name] = math[name]; + } else { + delete math.expression.mathWithTransform[name]; + } + } + /** + * Create a wrapper a round an function which converts the arguments + * to their primitive values (like convert a Matrix to Array) + * @param {Function} fn + * @return {Function} Returns the wrapped function + * @private + */ + + + function _wrap(fn) { + var wrapper = function wrapper() { + var args = []; + + for (var i = 0, len = arguments.length; i < len; i++) { + var arg = arguments[i]; + args[i] = arg && arg.valueOf(); + } + + return fn.apply(math, args); + }; + + if (fn.transform) { + wrapper.transform = fn.transform; + } + + return wrapper; + } + /** + * Import an instance of a factory into math.js + * @param {{factory: Function, name: string, path: string, math: boolean}} factory + * @param {Object} options See import for a description of the options + * @private + */ + + + function _importFactory(factory, options) { + if (typeof factory.name === 'string') { + var name = factory.name; + var existingTransform = name in math.expression.transform; + var namespace = factory.path ? traverse(math, factory.path) : math; + var existing = namespace.hasOwnProperty(name) ? namespace[name] : undefined; + + var resolver = function resolver() { + var instance = load(factory); + + if (instance && typeof instance.transform === 'function') { + throw new Error('Transforms cannot be attached to factory functions. ' + 'Please create a separate function for it with exports.path="expression.transform"'); + } + + if (isTypedFunction(existing) && isTypedFunction(instance)) { + if (options.override) {// replace the existing typed function (nothing to do) + } else { + // merge the existing and new typed function + instance = typed(existing, instance); + } + + return instance; + } + + if (existing === undefined || options.override) { + return instance; + } + + if (!options.silent) { + throw new Error('Cannot import "' + name + '": already exists'); + } + }; + + if (factory.lazy !== false) { + lazy(namespace, name, resolver); + + if (existingTransform) { + _deleteTransform(name); + } else { + if (factory.path === 'expression.transform' || factoryAllowedInExpressions(factory)) { + lazy(math.expression.mathWithTransform, name, resolver); + } + } + } else { + namespace[name] = resolver(); + + if (existingTransform) { + _deleteTransform(name); + } else { + if (factory.path === 'expression.transform' || factoryAllowedInExpressions(factory)) { + math.expression.mathWithTransform[name] = resolver(); + } + } + } + + math.emit('import', name, resolver, factory.path); + } else { + // unnamed factory. + // no lazy loading + load(factory); + } + } + /** + * Check whether given object is a type which can be imported + * @param {Function | number | string | boolean | null | Unit | Complex} object + * @return {boolean} + * @private + */ + + + function isSupportedType(object) { + return typeof object === 'function' || typeof object === 'number' || typeof object === 'string' || typeof object === 'boolean' || object === null || object && type.isUnit(object) || object && type.isComplex(object) || object && type.isBigNumber(object) || object && type.isFraction(object) || object && type.isMatrix(object) || object && Array.isArray(object); + } + /** + * Test whether a given thing is a typed-function + * @param {*} fn + * @return {boolean} Returns true when `fn` is a typed-function + */ + + + function isTypedFunction(fn) { + return typeof fn === 'function' && _typeof(fn.signatures) === 'object'; + } + + function allowedInExpressions(name) { + return !unsafe.hasOwnProperty(name); + } + + function factoryAllowedInExpressions(factory) { + return factory.path === undefined && !unsafe.hasOwnProperty(factory.name); + } // namespaces and functions not available in the parser for safety reasons + + + var unsafe = { + 'expression': true, + 'type': true, + 'docs': true, + 'error': true, + 'json': true, + 'chain': true // chain method not supported. Note that there is a unit chain too. + + }; + return mathImport; +} + +exports.math = true; // request access to the math namespace as 5th argument of the factory function + +exports.name = 'import'; +exports.factory = factory; +exports.lazy = true; +},{"../../error/ArgumentsError":8,"../../utils/object":55}],7:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var typedFunction = require('typed-function'); + +var digits = require('./../utils/number').digits; + +var isBigNumber = require('./../utils/bignumber/isBigNumber'); + +var isMatrix = require('./../utils/collection/isMatrix'); // returns a new instance of typed-function + + +var _createTyped = function createTyped() { + // initially, return the original instance of typed-function + // consecutively, return a new instance from typed.create. + _createTyped = typedFunction.create; + return typedFunction; +}; +/** + * Factory function for creating a new typed instance + * @param {Object} type Object with data types like Complex and BigNumber + * @returns {Function} + */ + + +exports.create = function create(type) { + // TODO: typed-function must be able to silently ignore signatures with unknown data types + // type checks for all known types + // + // note that: + // + // - check by duck-typing on a property like `isUnit`, instead of checking instanceof. + // instanceof cannot be used because that would not allow to pass data from + // one instance of math.js to another since each has it's own instance of Unit. + // - check the `isUnit` property via the constructor, so there will be no + // matches for "fake" instances like plain objects with a property `isUnit`. + // That is important for security reasons. + // - It must not be possible to override the type checks used internally, + // for security reasons, so these functions are not exposed in the expression + // parser. + type.isNumber = function (x) { + return typeof x === 'number'; + }; + + type.isComplex = function (x) { + return type.Complex && x instanceof type.Complex || false; + }; + + type.isBigNumber = isBigNumber; + + type.isFraction = function (x) { + return type.Fraction && x instanceof type.Fraction || false; + }; + + type.isUnit = function (x) { + return x && x.constructor.prototype.isUnit || false; + }; + + type.isString = function (x) { + return typeof x === 'string'; + }; + + type.isArray = Array.isArray; + type.isMatrix = isMatrix; + + type.isDenseMatrix = function (x) { + return x && x.isDenseMatrix && x.constructor.prototype.isMatrix || false; + }; + + type.isSparseMatrix = function (x) { + return x && x.isSparseMatrix && x.constructor.prototype.isMatrix || false; + }; + + type.isRange = function (x) { + return x && x.constructor.prototype.isRange || false; + }; + + type.isIndex = function (x) { + return x && x.constructor.prototype.isIndex || false; + }; + + type.isBoolean = function (x) { + return typeof x === 'boolean'; + }; + + type.isResultSet = function (x) { + return x && x.constructor.prototype.isResultSet || false; + }; + + type.isHelp = function (x) { + return x && x.constructor.prototype.isHelp || false; + }; + + type.isFunction = function (x) { + return typeof x === 'function'; + }; + + type.isDate = function (x) { + return x instanceof Date; + }; + + type.isRegExp = function (x) { + return x instanceof RegExp; + }; + + type.isObject = function (x) { + return _typeof(x) === 'object' && x.constructor === Object && !type.isComplex(x) && !type.isFraction(x); + }; + + type.isNull = function (x) { + return x === null; + }; + + type.isUndefined = function (x) { + return x === undefined; + }; + + type.isAccessorNode = function (x) { + return x && x.isAccessorNode && x.constructor.prototype.isNode || false; + }; + + type.isArrayNode = function (x) { + return x && x.isArrayNode && x.constructor.prototype.isNode || false; + }; + + type.isAssignmentNode = function (x) { + return x && x.isAssignmentNode && x.constructor.prototype.isNode || false; + }; + + type.isBlockNode = function (x) { + return x && x.isBlockNode && x.constructor.prototype.isNode || false; + }; + + type.isConditionalNode = function (x) { + return x && x.isConditionalNode && x.constructor.prototype.isNode || false; + }; + + type.isConstantNode = function (x) { + return x && x.isConstantNode && x.constructor.prototype.isNode || false; + }; + + type.isFunctionAssignmentNode = function (x) { + return x && x.isFunctionAssignmentNode && x.constructor.prototype.isNode || false; + }; + + type.isFunctionNode = function (x) { + return x && x.isFunctionNode && x.constructor.prototype.isNode || false; + }; + + type.isIndexNode = function (x) { + return x && x.isIndexNode && x.constructor.prototype.isNode || false; + }; + + type.isNode = function (x) { + return x && x.isNode && x.constructor.prototype.isNode || false; + }; + + type.isObjectNode = function (x) { + return x && x.isObjectNode && x.constructor.prototype.isNode || false; + }; + + type.isOperatorNode = function (x) { + return x && x.isOperatorNode && x.constructor.prototype.isNode || false; + }; + + type.isParenthesisNode = function (x) { + return x && x.isParenthesisNode && x.constructor.prototype.isNode || false; + }; + + type.isRangeNode = function (x) { + return x && x.isRangeNode && x.constructor.prototype.isNode || false; + }; + + type.isSymbolNode = function (x) { + return x && x.isSymbolNode && x.constructor.prototype.isNode || false; + }; + + type.isChain = function (x) { + return x && x.constructor.prototype.isChain || false; + }; // get a new instance of typed-function + + + var typed = _createTyped(); // define all types. The order of the types determines in which order function + // arguments are type-checked (so for performance it's important to put the + // most used types first). + + + typed.types = [{ + name: 'number', + test: type.isNumber + }, { + name: 'Complex', + test: type.isComplex + }, { + name: 'BigNumber', + test: type.isBigNumber + }, { + name: 'Fraction', + test: type.isFraction + }, { + name: 'Unit', + test: type.isUnit + }, { + name: 'string', + test: type.isString + }, { + name: 'Array', + test: type.isArray + }, { + name: 'Matrix', + test: type.isMatrix + }, { + name: 'DenseMatrix', + test: type.isDenseMatrix + }, { + name: 'SparseMatrix', + test: type.isSparseMatrix + }, { + name: 'Range', + test: type.isRange + }, { + name: 'Index', + test: type.isIndex + }, { + name: 'boolean', + test: type.isBoolean + }, { + name: 'ResultSet', + test: type.isResultSet + }, { + name: 'Help', + test: type.isHelp + }, { + name: 'function', + test: type.isFunction + }, { + name: 'Date', + test: type.isDate + }, { + name: 'RegExp', + test: type.isRegExp + }, { + name: 'null', + test: type.isNull + }, { + name: 'undefined', + test: type.isUndefined + }, { + name: 'OperatorNode', + test: type.isOperatorNode + }, { + name: 'ConstantNode', + test: type.isConstantNode + }, { + name: 'SymbolNode', + test: type.isSymbolNode + }, { + name: 'ParenthesisNode', + test: type.isParenthesisNode + }, { + name: 'FunctionNode', + test: type.isFunctionNode + }, { + name: 'FunctionAssignmentNode', + test: type.isFunctionAssignmentNode + }, { + name: 'ArrayNode', + test: type.isArrayNode + }, { + name: 'AssignmentNode', + test: type.isAssignmentNode + }, { + name: 'BlockNode', + test: type.isBlockNode + }, { + name: 'ConditionalNode', + test: type.isConditionalNode + }, { + name: 'IndexNode', + test: type.isIndexNode + }, { + name: 'RangeNode', + test: type.isRangeNode + }, { + name: 'Node', + test: type.isNode + }, { + name: 'Object', + test: type.isObject // order 'Object' last, it matches on other classes too + + }]; // TODO: add conversion from BigNumber to number? + + typed.conversions = [{ + from: 'number', + to: 'BigNumber', + convert: function convert(x) { + // note: conversion from number to BigNumber can fail if x has >15 digits + if (digits(x) > 15) { + throw new TypeError('Cannot implicitly convert a number with >15 significant digits to BigNumber ' + '(value: ' + x + '). ' + 'Use function bignumber(x) to convert to BigNumber.'); + } + + return new type.BigNumber(x); + } + }, { + from: 'number', + to: 'Complex', + convert: function convert(x) { + return new type.Complex(x, 0); + } + }, { + from: 'number', + to: 'string', + convert: function convert(x) { + return x + ''; + } + }, { + from: 'BigNumber', + to: 'Complex', + convert: function convert(x) { + return new type.Complex(x.toNumber(), 0); + } + }, { + from: 'Fraction', + to: 'BigNumber', + convert: function convert(x) { + throw new TypeError('Cannot implicitly convert a Fraction to BigNumber or vice versa. ' + 'Use function bignumber(x) to convert to BigNumber or fraction(x) to convert to Fraction.'); + } + }, { + from: 'Fraction', + to: 'Complex', + convert: function convert(x) { + return new type.Complex(x.valueOf(), 0); + } + }, { + from: 'number', + to: 'Fraction', + convert: function convert(x) { + var f = new type.Fraction(x); + + if (f.valueOf() !== x) { + throw new TypeError('Cannot implicitly convert a number to a Fraction when there will be a loss of precision ' + '(value: ' + x + '). ' + 'Use function fraction(x) to convert to Fraction.'); + } + + return new type.Fraction(x); + } + }, { + // FIXME: add conversion from Fraction to number, for example for `sqrt(fraction(1,3))` + // from: 'Fraction', + // to: 'number', + // convert: function (x) { + // return x.valueOf() + // } + // }, { + from: 'string', + to: 'number', + convert: function convert(x) { + var n = Number(x); + + if (isNaN(n)) { + throw new Error('Cannot convert "' + x + '" to a number'); + } + + return n; + } + }, { + from: 'string', + to: 'BigNumber', + convert: function convert(x) { + try { + return new type.BigNumber(x); + } catch (err) { + throw new Error('Cannot convert "' + x + '" to BigNumber'); + } + } + }, { + from: 'string', + to: 'Fraction', + convert: function convert(x) { + try { + return new type.Fraction(x); + } catch (err) { + throw new Error('Cannot convert "' + x + '" to Fraction'); + } + } + }, { + from: 'string', + to: 'Complex', + convert: function convert(x) { + try { + return new type.Complex(x); + } catch (err) { + throw new Error('Cannot convert "' + x + '" to Complex'); + } + } + }, { + from: 'boolean', + to: 'number', + convert: function convert(x) { + return +x; + } + }, { + from: 'boolean', + to: 'BigNumber', + convert: function convert(x) { + return new type.BigNumber(+x); + } + }, { + from: 'boolean', + to: 'Fraction', + convert: function convert(x) { + return new type.Fraction(+x); + } + }, { + from: 'boolean', + to: 'string', + convert: function convert(x) { + return +x; + } + }, { + from: 'Array', + to: 'Matrix', + convert: function convert(array) { + return new type.DenseMatrix(array); + } + }, { + from: 'Matrix', + to: 'Array', + convert: function convert(matrix) { + return matrix.valueOf(); + } + }]; + return typed; +}; +},{"./../utils/bignumber/isBigNumber":48,"./../utils/collection/isMatrix":50,"./../utils/number":54,"typed-function":59}],8:[function(require,module,exports){ +'use strict'; +/** + * Create a syntax error with the message: + * 'Wrong number of arguments in function <fn> (<count> provided, <min>-<max> expected)' + * @param {string} fn Function name + * @param {number} count Actual argument count + * @param {number} min Minimum required argument count + * @param {number} [max] Maximum required argument count + * @extends Error + */ + +function ArgumentsError(fn, count, min, max) { + if (!(this instanceof ArgumentsError)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this.fn = fn; + this.count = count; + this.min = min; + this.max = max; + this.message = 'Wrong number of arguments in function ' + fn + ' (' + count + ' provided, ' + min + (max !== undefined && max !== null ? '-' + max : '') + ' expected)'; + this.stack = new Error().stack; +} + +ArgumentsError.prototype = new Error(); +ArgumentsError.prototype.constructor = Error; +ArgumentsError.prototype.name = 'ArgumentsError'; +ArgumentsError.prototype.isArgumentsError = true; +module.exports = ArgumentsError; +},{}],9:[function(require,module,exports){ +'use strict'; +/** + * Create a range error with the message: + * 'Dimension mismatch (<actual size> != <expected size>)' + * @param {number | number[]} actual The actual size + * @param {number | number[]} expected The expected size + * @param {string} [relation='!='] Optional relation between actual + * and expected size: '!=', '<', etc. + * @extends RangeError + */ + +function DimensionError(actual, expected, relation) { + if (!(this instanceof DimensionError)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this.actual = actual; + this.expected = expected; + this.relation = relation; + this.message = 'Dimension mismatch (' + (Array.isArray(actual) ? '[' + actual.join(', ') + ']' : actual) + ' ' + (this.relation || '!=') + ' ' + (Array.isArray(expected) ? '[' + expected.join(', ') + ']' : expected) + ')'; + this.stack = new Error().stack; +} + +DimensionError.prototype = new RangeError(); +DimensionError.prototype.constructor = RangeError; +DimensionError.prototype.name = 'DimensionError'; +DimensionError.prototype.isDimensionError = true; +module.exports = DimensionError; +},{}],10:[function(require,module,exports){ +'use strict'; +/** + * Create a range error with the message: + * 'Index out of range (index < min)' + * 'Index out of range (index < max)' + * + * @param {number} index The actual index + * @param {number} [min=0] Minimum index (included) + * @param {number} [max] Maximum index (excluded) + * @extends RangeError + */ + +function IndexError(index, min, max) { + if (!(this instanceof IndexError)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this.index = index; + + if (arguments.length < 3) { + this.min = 0; + this.max = min; + } else { + this.min = min; + this.max = max; + } + + if (this.min !== undefined && this.index < this.min) { + this.message = 'Index out of range (' + this.index + ' < ' + this.min + ')'; + } else if (this.max !== undefined && this.index >= this.max) { + this.message = 'Index out of range (' + this.index + ' > ' + (this.max - 1) + ')'; + } else { + this.message = 'Index out of range (' + this.index + ')'; + } + + this.stack = new Error().stack; +} + +IndexError.prototype = new RangeError(); +IndexError.prototype.constructor = RangeError; +IndexError.prototype.name = 'IndexError'; +IndexError.prototype.isIndexError = true; +module.exports = IndexError; +},{}],11:[function(require,module,exports){ +'use strict'; + +var deepMap = require('../../utils/collection/deepMap'); + +function factory(type, config, load, typed) { + var parse = load(require('../parse')); + /** + * Parse and compile an expression. + * Returns a an object with a function `eval([scope])` to evaluate the + * compiled expression. + * + * Syntax: + * + * math.compile(expr) // returns one node + * math.compile([expr1, expr2, expr3, ...]) // returns an array with nodes + * + * Examples: + * + * const code1 = math.compile('sqrt(3^2 + 4^2)') + * code1.eval() // 5 + * + * let scope = {a: 3, b: 4} + * const code2 = math.compile('a * b') // 12 + * code2.eval(scope) // 12 + * scope.a = 5 + * code2.eval(scope) // 20 + * + * const nodes = math.compile(['a = 3', 'b = 4', 'a * b']) + * nodes[2].eval() // 12 + * + * See also: + * + * parse, eval + * + * @param {string | string[] | Array | Matrix} expr + * The expression to be compiled + * @return {{eval: Function} | Array.<{eval: Function}>} code + * An object with the compiled expression + * @throws {Error} + */ + + return typed('compile', { + 'string': function string(expr) { + return parse(expr).compile(); + }, + 'Array | Matrix': function ArrayMatrix(expr) { + return deepMap(expr, function (entry) { + return parse(entry).compile(); + }); + } + }); +} + +exports.name = 'compile'; +exports.factory = factory; +},{"../../utils/collection/deepMap":49,"../parse":34}],12:[function(require,module,exports){ +'use strict'; + +var deepMap = require('../../utils/collection/deepMap'); + +function factory(type, config, load, typed) { + var parse = load(require('../parse')); + /** + * Evaluate an expression. + * + * Note the evaluating arbitrary expressions may involve security risks, + * see [http://mathjs.org/docs/expressions/security.html](http://mathjs.org/docs/expressions/security.html) for more information. + * + * Syntax: + * + * math.eval(expr) + * math.eval(expr, scope) + * math.eval([expr1, expr2, expr3, ...]) + * math.eval([expr1, expr2, expr3, ...], scope) + * + * Example: + * + * math.eval('(2+3)/4') // 1.25 + * math.eval('sqrt(3^2 + 4^2)') // 5 + * math.eval('sqrt(-4)') // 2i + * math.eval(['a=3', 'b=4', 'a*b']) // [3, 4, 12] + * + * let scope = {a:3, b:4} + * math.eval('a * b', scope) // 12 + * + * See also: + * + * parse, compile + * + * @param {string | string[] | Matrix} expr The expression to be evaluated + * @param {Object} [scope] Scope to read/write variables + * @return {*} The result of the expression + * @throws {Error} + */ + + return typed('compile', { + 'string': function string(expr) { + var scope = {}; + return parse(expr).compile().eval(scope); + }, + 'string, Object': function stringObject(expr, scope) { + return parse(expr).compile().eval(scope); + }, + 'Array | Matrix': function ArrayMatrix(expr) { + var scope = {}; + return deepMap(expr, function (entry) { + return parse(entry).compile().eval(scope); + }); + }, + 'Array | Matrix, Object': function ArrayMatrixObject(expr, scope) { + return deepMap(expr, function (entry) { + return parse(entry).compile().eval(scope); + }); + } + }); +} + +exports.name = 'eval'; +exports.factory = factory; +},{"../../utils/collection/deepMap":49,"../parse":34}],13:[function(require,module,exports){ +'use strict'; + +function factory(type, config, load, typed) { + var parse = load(require('../parse')); + /** + * Parse an expression. Returns a node tree, which can be evaluated by + * invoking node.eval(). + * + * Note the evaluating arbitrary expressions may involve security risks, + * see [http://mathjs.org/docs/expressions/security.html](http://mathjs.org/docs/expressions/security.html) for more information. + * + * Syntax: + * + * math.parse(expr) + * math.parse(expr, options) + * math.parse([expr1, expr2, expr3, ...]) + * math.parse([expr1, expr2, expr3, ...], options) + * + * Example: + * + * const node1 = math.parse('sqrt(3^2 + 4^2)') + * node1.compile().eval() // 5 + * + * let scope = {a:3, b:4} + * const node2 = math.parse('a * b') // 12 + * const code2 = node2.compile() + * code2.eval(scope) // 12 + * scope.a = 5 + * code2.eval(scope) // 20 + * + * const nodes = math.parse(['a = 3', 'b = 4', 'a * b']) + * nodes[2].compile().eval() // 12 + * + * See also: + * + * eval, compile + * + * @param {string | string[] | Matrix} expr Expression to be parsed + * @param {{nodes: Object<string, Node>}} [options] Available options: + * - `nodes` a set of custom nodes + * @return {Node | Node[]} node + * @throws {Error} + */ + + return typed('parse', { + 'string | Array | Matrix': parse, + 'string | Array | Matrix, Object': parse + }); +} + +exports.name = 'parse'; +exports.factory = factory; +},{"../parse":34}],14:[function(require,module,exports){ +'use strict'; // Reserved keywords not allowed to use in the parser + +module.exports = { + end: true +}; +},{}],15:[function(require,module,exports){ +'use strict'; + +var getSafeProperty = require('../../utils/customs').getSafeProperty; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + var access = load(require('./utils/access')); + /** + * @constructor AccessorNode + * @extends {Node} + * Access an object property or get a matrix subset + * + * @param {Node} object The object from which to retrieve + * a property or subset. + * @param {IndexNode} index IndexNode containing ranges + */ + + function AccessorNode(object, index) { + if (!(this instanceof AccessorNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + if (!type.isNode(object)) { + throw new TypeError('Node expected for parameter "object"'); + } + + if (!type.isIndexNode(index)) { + throw new TypeError('IndexNode expected for parameter "index"'); + } + + this.object = object || null; + this.index = index; // readonly property name + + Object.defineProperty(this, 'name', { + get: function () { + if (this.index) { + return this.index.isObjectProperty() ? this.index.getObjectProperty() : ''; + } else { + return this.object.name || ''; + } + }.bind(this), + set: function set() { + throw new Error('Cannot assign a new name, name is read-only'); + } + }); + } + + AccessorNode.prototype = new Node(); + AccessorNode.prototype.type = 'AccessorNode'; + AccessorNode.prototype.isAccessorNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + AccessorNode.prototype._compile = function (math, argNames) { + var evalObject = this.object._compile(math, argNames); + + var evalIndex = this.index._compile(math, argNames); + + if (this.index.isObjectProperty()) { + var prop = this.index.getObjectProperty(); + return function evalAccessorNode(scope, args, context) { + return getSafeProperty(evalObject(scope, args, context), prop); + }; + } else { + return function evalAccessorNode(scope, args, context) { + var object = evalObject(scope, args, context); + var index = evalIndex(scope, args, object); // we pass object here instead of context + + return access(object, index); + }; + } + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + AccessorNode.prototype.forEach = function (callback) { + callback(this.object, 'object', this); + callback(this.index, 'index', this); + }; + /** + * Create a new AccessorNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {AccessorNode} Returns a transformed copy of the node + */ + + + AccessorNode.prototype.map = function (callback) { + return new AccessorNode(this._ifNode(callback(this.object, 'object', this)), this._ifNode(callback(this.index, 'index', this))); + }; + /** + * Create a clone of this node, a shallow copy + * @return {AccessorNode} + */ + + + AccessorNode.prototype.clone = function () { + return new AccessorNode(this.object, this.index); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} + */ + + + AccessorNode.prototype._toString = function (options) { + var object = this.object.toString(options); + + if (needParenthesis(this.object)) { + object = '(' + object + ')'; + } + + return object + this.index.toString(options); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} + */ + + + AccessorNode.prototype.toHTML = function (options) { + var object = this.object.toHTML(options); + + if (needParenthesis(this.object)) { + object = '<span class="math-parenthesis math-round-parenthesis">(</span>' + object + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + return object + this.index.toHTML(options); + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} + */ + + + AccessorNode.prototype._toTex = function (options) { + var object = this.object.toTex(options); + + if (needParenthesis(this.object)) { + object = "\\left(' + object + '\\right)"; + } + + return object + this.index.toTex(options); + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + AccessorNode.prototype.toJSON = function () { + return { + mathjs: 'AccessorNode', + object: this.object, + index: this.index + }; + }; + /** + * Instantiate an AccessorNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "AccessorNode", object: ..., index: ...}`, + * where mathjs is optional + * @returns {AccessorNode} + */ + + + AccessorNode.fromJSON = function (json) { + return new AccessorNode(json.object, json.index); + }; + /** + * Are parenthesis needed? + * @private + */ + + + function needParenthesis(node) { + // TODO: maybe make a method on the nodes which tells whether they need parenthesis? + return !(type.isAccessorNode(node) || type.isArrayNode(node) || type.isConstantNode(node) || type.isFunctionNode(node) || type.isObjectNode(node) || type.isParenthesisNode(node) || type.isSymbolNode(node)); + } + + return AccessorNode; +} + +exports.name = 'AccessorNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../utils/customs":51,"./Node":24,"./utils/access":31}],16:[function(require,module,exports){ +'use strict'; + +var map = require('../../utils/array').map; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + /** + * @constructor ArrayNode + * @extends {Node} + * Holds an 1-dimensional array with items + * @param {Node[]} [items] 1 dimensional array with items + */ + + function ArrayNode(items) { + if (!(this instanceof ArrayNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this.items = items || []; // validate input + + if (!Array.isArray(this.items) || !this.items.every(type.isNode)) { + throw new TypeError('Array containing Nodes expected'); + } // TODO: deprecated since v3, remove some day + + + var deprecated = function deprecated() { + throw new Error('Property `ArrayNode.nodes` is deprecated, use `ArrayNode.items` instead'); + }; + + Object.defineProperty(this, 'nodes', { + get: deprecated, + set: deprecated + }); + } + + ArrayNode.prototype = new Node(); + ArrayNode.prototype.type = 'ArrayNode'; + ArrayNode.prototype.isArrayNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + ArrayNode.prototype._compile = function (math, argNames) { + var evalItems = map(this.items, function (item) { + return item._compile(math, argNames); + }); + var asMatrix = math.config().matrix !== 'Array'; + + if (asMatrix) { + var matrix = math.matrix; + return function evalArrayNode(scope, args, context) { + return matrix(map(evalItems, function (evalItem) { + return evalItem(scope, args, context); + })); + }; + } else { + return function evalArrayNode(scope, args, context) { + return map(evalItems, function (evalItem) { + return evalItem(scope, args, context); + }); + }; + } + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + ArrayNode.prototype.forEach = function (callback) { + for (var i = 0; i < this.items.length; i++) { + var node = this.items[i]; + callback(node, 'items[' + i + ']', this); + } + }; + /** + * Create a new ArrayNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {ArrayNode} Returns a transformed copy of the node + */ + + + ArrayNode.prototype.map = function (callback) { + var items = []; + + for (var i = 0; i < this.items.length; i++) { + items[i] = this._ifNode(callback(this.items[i], 'items[' + i + ']', this)); + } + + return new ArrayNode(items); + }; + /** + * Create a clone of this node, a shallow copy + * @return {ArrayNode} + */ + + + ArrayNode.prototype.clone = function () { + return new ArrayNode(this.items.slice(0)); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + * @override + */ + + + ArrayNode.prototype._toString = function (options) { + var items = this.items.map(function (node) { + return node.toString(options); + }); + return '[' + items.join(', ') + ']'; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + ArrayNode.prototype.toJSON = function () { + return { + mathjs: 'ArrayNode', + items: this.items + }; + }; + /** + * Instantiate an ArrayNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "ArrayNode", items: [...]}`, + * where mathjs is optional + * @returns {ArrayNode} + */ + + + ArrayNode.fromJSON = function (json) { + return new ArrayNode(json.items); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + * @override + */ + + + ArrayNode.prototype.toHTML = function (options) { + var items = this.items.map(function (node) { + return node.toHTML(options); + }); + return '<span class="math-parenthesis math-square-parenthesis">[</span>' + items.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-square-parenthesis">]</span>'; + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + ArrayNode.prototype._toTex = function (options) { + var s = '\\begin{bmatrix}'; + this.items.forEach(function (node) { + if (node.items) { + s += node.items.map(function (childNode) { + return childNode.toTex(options); + }).join('&'); + } else { + s += node.toTex(options); + } // new line + + + s += '\\\\'; + }); + s += '\\end{bmatrix}'; + return s; + }; + + return ArrayNode; +} + +exports.name = 'ArrayNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../utils/array":46,"./Node":24}],17:[function(require,module,exports){ +'use strict'; + +var getSafeProperty = require('../../utils/customs').getSafeProperty; + +var setSafeProperty = require('../../utils/customs').setSafeProperty; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + var assign = load(require('./utils/assign')); + var access = load(require('./utils/access')); + + var operators = require('../operators'); + /** + * @constructor AssignmentNode + * @extends {Node} + * + * Define a symbol, like `a=3.2`, update a property like `a.b=3.2`, or + * replace a subset of a matrix like `A[2,2]=42`. + * + * Syntax: + * + * new AssignmentNode(symbol, value) + * new AssignmentNode(object, index, value) + * + * Usage: + * + * new AssignmentNode(new SymbolNode('a'), new ConstantNode(2)) // a=2 + * new AssignmentNode(new SymbolNode('a'), new IndexNode('b'), new ConstantNode(2)) // a.b=2 + * new AssignmentNode(new SymbolNode('a'), new IndexNode(1, 2), new ConstantNode(3)) // a[1,2]=3 + * + * @param {SymbolNode | AccessorNode} object Object on which to assign a value + * @param {IndexNode} [index=null] Index, property name or matrix + * index. Optional. If not provided + * and `object` is a SymbolNode, + * the property is assigned to the + * global scope. + * @param {Node} value The value to be assigned + */ + + + function AssignmentNode(object, index, value) { + if (!(this instanceof AssignmentNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this.object = object; + this.index = value ? index : null; + this.value = value || index; // validate input + + if (!type.isSymbolNode(object) && !type.isAccessorNode(object)) { + throw new TypeError('SymbolNode or AccessorNode expected as "object"'); + } + + if (type.isSymbolNode(object) && object.name === 'end') { + throw new Error('Cannot assign to symbol "end"'); + } + + if (this.index && !type.isIndexNode(this.index)) { + // index is optional + throw new TypeError('IndexNode expected as "index"'); + } + + if (!type.isNode(this.value)) { + throw new TypeError('Node expected as "value"'); + } // readonly property name + + + Object.defineProperty(this, 'name', { + get: function () { + if (this.index) { + return this.index.isObjectProperty() ? this.index.getObjectProperty() : ''; + } else { + return this.object.name || ''; + } + }.bind(this), + set: function set() { + throw new Error('Cannot assign a new name, name is read-only'); + } + }); + } + + AssignmentNode.prototype = new Node(); + AssignmentNode.prototype.type = 'AssignmentNode'; + AssignmentNode.prototype.isAssignmentNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + AssignmentNode.prototype._compile = function (math, argNames) { + var evalObject = this.object._compile(math, argNames); + + var evalIndex = this.index ? this.index._compile(math, argNames) : null; + + var evalValue = this.value._compile(math, argNames); + + var name = this.object.name; + + if (!this.index) { + // apply a variable to the scope, for example `a=2` + if (!type.isSymbolNode(this.object)) { + throw new TypeError('SymbolNode expected as object'); + } + + return function evalAssignmentNode(scope, args, context) { + return setSafeProperty(scope, name, evalValue(scope, args, context)); + }; + } else if (this.index.isObjectProperty()) { + // apply an object property for example `a.b=2` + var prop = this.index.getObjectProperty(); + return function evalAssignmentNode(scope, args, context) { + var object = evalObject(scope, args, context); + var value = evalValue(scope, args, context); + return setSafeProperty(object, prop, value); + }; + } else if (type.isSymbolNode(this.object)) { + // update a matrix subset, for example `a[2]=3` + return function evalAssignmentNode(scope, args, context) { + var childObject = evalObject(scope, args, context); + var value = evalValue(scope, args, context); + var index = evalIndex(scope, args, childObject); // Important: we pass childObject instead of context + + setSafeProperty(scope, name, assign(childObject, index, value)); + return value; + }; + } else { + // type.isAccessorNode(node.object) === true + // update a matrix subset, for example `a.b[2]=3` + // we will not use the compile function of the AccessorNode, but compile it + // ourselves here as we need the parent object of the AccessorNode: + // wee need to apply the updated object to parent object + var evalParentObject = this.object.object._compile(math, argNames); + + if (this.object.index.isObjectProperty()) { + var parentProp = this.object.index.getObjectProperty(); + return function evalAssignmentNode(scope, args, context) { + var parent = evalParentObject(scope, args, context); + var childObject = getSafeProperty(parent, parentProp); + var index = evalIndex(scope, args, childObject); // Important: we pass childObject instead of context + + var value = evalValue(scope, args, context); + setSafeProperty(parent, parentProp, assign(childObject, index, value)); + return value; + }; + } else { + // if some parameters use the 'end' parameter, we need to calculate the size + var evalParentIndex = this.object.index._compile(math, argNames); + + return function evalAssignmentNode(scope, args, context) { + var parent = evalParentObject(scope, args, context); + var parentIndex = evalParentIndex(scope, args, parent); // Important: we pass parent instead of context + + var childObject = access(parent, parentIndex); + var index = evalIndex(scope, args, childObject); // Important: we pass childObject instead of context + + var value = evalValue(scope, args, context); + assign(parent, parentIndex, assign(childObject, index, value)); + return value; + }; + } + } + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + AssignmentNode.prototype.forEach = function (callback) { + callback(this.object, 'object', this); + + if (this.index) { + callback(this.index, 'index', this); + } + + callback(this.value, 'value', this); + }; + /** + * Create a new AssignmentNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {AssignmentNode} Returns a transformed copy of the node + */ + + + AssignmentNode.prototype.map = function (callback) { + var object = this._ifNode(callback(this.object, 'object', this)); + + var index = this.index ? this._ifNode(callback(this.index, 'index', this)) : null; + + var value = this._ifNode(callback(this.value, 'value', this)); + + return new AssignmentNode(object, index, value); + }; + /** + * Create a clone of this node, a shallow copy + * @return {AssignmentNode} + */ + + + AssignmentNode.prototype.clone = function () { + return new AssignmentNode(this.object, this.index, this.value); + }; + /* + * Is parenthesis needed? + * @param {node} node + * @param {string} [parenthesis='keep'] + * @private + */ + + + function needParenthesis(node, parenthesis) { + if (!parenthesis) { + parenthesis = 'keep'; + } + + var precedence = operators.getPrecedence(node, parenthesis); + var exprPrecedence = operators.getPrecedence(node.value, parenthesis); + return parenthesis === 'all' || exprPrecedence !== null && exprPrecedence <= precedence; + } + /** + * Get string representation + * @param {Object} options + * @return {string} + */ + + + AssignmentNode.prototype._toString = function (options) { + var object = this.object.toString(options); + var index = this.index ? this.index.toString(options) : ''; + var value = this.value.toString(options); + + if (needParenthesis(this, options && options.parenthesis)) { + value = '(' + value + ')'; + } + + return object + index + ' = ' + value; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + AssignmentNode.prototype.toJSON = function () { + return { + mathjs: 'AssignmentNode', + object: this.object, + index: this.index, + value: this.value + }; + }; + /** + * Instantiate an AssignmentNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "AssignmentNode", object: ..., index: ..., value: ...}`, + * where mathjs is optional + * @returns {AssignmentNode} + */ + + + AssignmentNode.fromJSON = function (json) { + return new AssignmentNode(json.object, json.index, json.value); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} + */ + + + AssignmentNode.prototype.toHTML = function (options) { + var object = this.object.toHTML(options); + var index = this.index ? this.index.toHTML(options) : ''; + var value = this.value.toHTML(options); + + if (needParenthesis(this, options && options.parenthesis)) { + value = '<span class="math-paranthesis math-round-parenthesis">(</span>' + value + '<span class="math-paranthesis math-round-parenthesis">)</span>'; + } + + return object + index + '<span class="math-operator math-assignment-operator math-variable-assignment-operator math-binary-operator">=</span>' + value; + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} + */ + + + AssignmentNode.prototype._toTex = function (options) { + var object = this.object.toTex(options); + var index = this.index ? this.index.toTex(options) : ''; + var value = this.value.toTex(options); + + if (needParenthesis(this, options && options.parenthesis)) { + value = "\\left(".concat(value, "\\right)"); + } + + return object + index + ':=' + value; + }; + + return AssignmentNode; +} + +exports.name = 'AssignmentNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../utils/customs":51,"../operators":33,"./Node":24,"./utils/access":31,"./utils/assign":32}],18:[function(require,module,exports){ +'use strict'; + +var forEach = require('../../utils/array').forEach; + +var map = require('../../utils/array').map; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + var ResultSet = load(require('../../type/resultset/ResultSet')); + /** + * @constructor BlockNode + * @extends {Node} + * Holds a set with blocks + * @param {Array.<{node: Node} | {node: Node, visible: boolean}>} blocks + * An array with blocks, where a block is constructed as an Object + * with properties block, which is a Node, and visible, which is + * a boolean. The property visible is optional and is true by default + */ + + function BlockNode(blocks) { + if (!(this instanceof BlockNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } // validate input, copy blocks + + + if (!Array.isArray(blocks)) throw new Error('Array expected'); + this.blocks = blocks.map(function (block) { + var node = block && block.node; + var visible = block && block.visible !== undefined ? block.visible : true; + if (!type.isNode(node)) throw new TypeError('Property "node" must be a Node'); + if (typeof visible !== 'boolean') throw new TypeError('Property "visible" must be a boolean'); + return { + node: node, + visible: visible + }; + }); + } + + BlockNode.prototype = new Node(); + BlockNode.prototype.type = 'BlockNode'; + BlockNode.prototype.isBlockNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + BlockNode.prototype._compile = function (math, argNames) { + var evalBlocks = map(this.blocks, function (block) { + return { + eval: block.node._compile(math, argNames), + visible: block.visible + }; + }); + return function evalBlockNodes(scope, args, context) { + var results = []; + forEach(evalBlocks, function evalBlockNode(block) { + var result = block.eval(scope, args, context); + + if (block.visible) { + results.push(result); + } + }); + return new ResultSet(results); + }; + }; + /** + * Execute a callback for each of the child blocks of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + BlockNode.prototype.forEach = function (callback) { + for (var i = 0; i < this.blocks.length; i++) { + callback(this.blocks[i].node, 'blocks[' + i + '].node', this); + } + }; + /** + * Create a new BlockNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {BlockNode} Returns a transformed copy of the node + */ + + + BlockNode.prototype.map = function (callback) { + var blocks = []; + + for (var i = 0; i < this.blocks.length; i++) { + var block = this.blocks[i]; + + var node = this._ifNode(callback(block.node, 'blocks[' + i + '].node', this)); + + blocks[i] = { + node: node, + visible: block.visible + }; + } + + return new BlockNode(blocks); + }; + /** + * Create a clone of this node, a shallow copy + * @return {BlockNode} + */ + + + BlockNode.prototype.clone = function () { + var blocks = this.blocks.map(function (block) { + return { + node: block.node, + visible: block.visible + }; + }); + return new BlockNode(blocks); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + * @override + */ + + + BlockNode.prototype._toString = function (options) { + return this.blocks.map(function (param) { + return param.node.toString(options) + (param.visible ? '' : ';'); + }).join('\n'); + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + BlockNode.prototype.toJSON = function () { + return { + mathjs: 'BlockNode', + blocks: this.blocks + }; + }; + /** + * Instantiate an BlockNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "BlockNode", blocks: [{node: ..., visible: false}, ...]}`, + * where mathjs is optional + * @returns {BlockNode} + */ + + + BlockNode.fromJSON = function (json) { + return new BlockNode(json.blocks); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + * @override + */ + + + BlockNode.prototype.toHTML = function (options) { + return this.blocks.map(function (param) { + return param.node.toHTML(options) + (param.visible ? '' : '<span class="math-separator">;</span>'); + }).join('<span class="math-separator"><br /></span>'); + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + BlockNode.prototype._toTex = function (options) { + return this.blocks.map(function (param) { + return param.node.toTex(options) + (param.visible ? '' : ';'); + }).join('\\;\\;\n'); + }; + + return BlockNode; +} + +exports.name = 'BlockNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../type/resultset/ResultSet":45,"../../utils/array":46,"./Node":24}],19:[function(require,module,exports){ +'use strict'; + +var operators = require('../operators'); + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + var mathTypeOf = load(require('../../function/utils/typeof')); + /** + * A lazy evaluating conditional operator: 'condition ? trueExpr : falseExpr' + * + * @param {Node} condition Condition, must result in a boolean + * @param {Node} trueExpr Expression evaluated when condition is true + * @param {Node} falseExpr Expression evaluated when condition is true + * + * @constructor ConditionalNode + * @extends {Node} + */ + + function ConditionalNode(condition, trueExpr, falseExpr) { + if (!(this instanceof ConditionalNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + if (!type.isNode(condition)) throw new TypeError('Parameter condition must be a Node'); + if (!type.isNode(trueExpr)) throw new TypeError('Parameter trueExpr must be a Node'); + if (!type.isNode(falseExpr)) throw new TypeError('Parameter falseExpr must be a Node'); + this.condition = condition; + this.trueExpr = trueExpr; + this.falseExpr = falseExpr; + } + + ConditionalNode.prototype = new Node(); + ConditionalNode.prototype.type = 'ConditionalNode'; + ConditionalNode.prototype.isConditionalNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + ConditionalNode.prototype._compile = function (math, argNames) { + var evalCondition = this.condition._compile(math, argNames); + + var evalTrueExpr = this.trueExpr._compile(math, argNames); + + var evalFalseExpr = this.falseExpr._compile(math, argNames); + + return function evalConditionalNode(scope, args, context) { + return testCondition(evalCondition(scope, args, context)) ? evalTrueExpr(scope, args, context) : evalFalseExpr(scope, args, context); + }; + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + ConditionalNode.prototype.forEach = function (callback) { + callback(this.condition, 'condition', this); + callback(this.trueExpr, 'trueExpr', this); + callback(this.falseExpr, 'falseExpr', this); + }; + /** + * Create a new ConditionalNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {ConditionalNode} Returns a transformed copy of the node + */ + + + ConditionalNode.prototype.map = function (callback) { + return new ConditionalNode(this._ifNode(callback(this.condition, 'condition', this)), this._ifNode(callback(this.trueExpr, 'trueExpr', this)), this._ifNode(callback(this.falseExpr, 'falseExpr', this))); + }; + /** + * Create a clone of this node, a shallow copy + * @return {ConditionalNode} + */ + + + ConditionalNode.prototype.clone = function () { + return new ConditionalNode(this.condition, this.trueExpr, this.falseExpr); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + */ + + + ConditionalNode.prototype._toString = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var precedence = operators.getPrecedence(this, parenthesis); // Enclose Arguments in parentheses if they are an OperatorNode + // or have lower or equal precedence + // NOTE: enclosing all OperatorNodes in parentheses is a decision + // purely based on aesthetics and readability + + var condition = this.condition.toString(options); + var conditionPrecedence = operators.getPrecedence(this.condition, parenthesis); + + if (parenthesis === 'all' || this.condition.type === 'OperatorNode' || conditionPrecedence !== null && conditionPrecedence <= precedence) { + condition = '(' + condition + ')'; + } + + var trueExpr = this.trueExpr.toString(options); + var truePrecedence = operators.getPrecedence(this.trueExpr, parenthesis); + + if (parenthesis === 'all' || this.trueExpr.type === 'OperatorNode' || truePrecedence !== null && truePrecedence <= precedence) { + trueExpr = '(' + trueExpr + ')'; + } + + var falseExpr = this.falseExpr.toString(options); + var falsePrecedence = operators.getPrecedence(this.falseExpr, parenthesis); + + if (parenthesis === 'all' || this.falseExpr.type === 'OperatorNode' || falsePrecedence !== null && falsePrecedence <= precedence) { + falseExpr = '(' + falseExpr + ')'; + } + + return condition + ' ? ' + trueExpr + ' : ' + falseExpr; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + ConditionalNode.prototype.toJSON = function () { + return { + mathjs: 'ConditionalNode', + condition: this.condition, + trueExpr: this.trueExpr, + falseExpr: this.falseExpr + }; + }; + /** + * Instantiate an ConditionalNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "ConditionalNode", "condition": ..., "trueExpr": ..., "falseExpr": ...}`, + * where mathjs is optional + * @returns {ConditionalNode} + */ + + + ConditionalNode.fromJSON = function (json) { + return new ConditionalNode(json.condition, json.trueExpr, json.falseExpr); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + */ + + + ConditionalNode.prototype.toHTML = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var precedence = operators.getPrecedence(this, parenthesis); // Enclose Arguments in parentheses if they are an OperatorNode + // or have lower or equal precedence + // NOTE: enclosing all OperatorNodes in parentheses is a decision + // purely based on aesthetics and readability + + var condition = this.condition.toHTML(options); + var conditionPrecedence = operators.getPrecedence(this.condition, parenthesis); + + if (parenthesis === 'all' || this.condition.type === 'OperatorNode' || conditionPrecedence !== null && conditionPrecedence <= precedence) { + condition = '<span class="math-parenthesis math-round-parenthesis">(</span>' + condition + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + var trueExpr = this.trueExpr.toHTML(options); + var truePrecedence = operators.getPrecedence(this.trueExpr, parenthesis); + + if (parenthesis === 'all' || this.trueExpr.type === 'OperatorNode' || truePrecedence !== null && truePrecedence <= precedence) { + trueExpr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + trueExpr + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + var falseExpr = this.falseExpr.toHTML(options); + var falsePrecedence = operators.getPrecedence(this.falseExpr, parenthesis); + + if (parenthesis === 'all' || this.falseExpr.type === 'OperatorNode' || falsePrecedence !== null && falsePrecedence <= precedence) { + falseExpr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + falseExpr + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + return condition + '<span class="math-operator math-conditional-operator">?</span>' + trueExpr + '<span class="math-operator math-conditional-operator">:</span>' + falseExpr; + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + ConditionalNode.prototype._toTex = function (options) { + return '\\begin{cases} {' + this.trueExpr.toTex(options) + '}, &\\quad{\\text{if }\\;' + this.condition.toTex(options) + '}\\\\{' + this.falseExpr.toTex(options) + '}, &\\quad{\\text{otherwise}}\\end{cases}'; + }; + /** + * Test whether a condition is met + * @param {*} condition + * @returns {boolean} true if condition is true or non-zero, else false + */ + + + function testCondition(condition) { + if (typeof condition === 'number' || typeof condition === 'boolean' || typeof condition === 'string') { + return !!condition; + } + + if (condition) { + if (type.isBigNumber(condition)) { + return !condition.isZero(); + } + + if (type.isComplex(condition)) { + return !!(condition.re || condition.im); + } + + if (type.isUnit(condition)) { + return !!condition.value; + } + } + + if (condition === null || condition === undefined) { + return false; + } + + throw new TypeError('Unsupported type of condition "' + mathTypeOf(condition) + '"'); + } + + return ConditionalNode; +} + +exports.name = 'ConditionalNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../function/utils/typeof":38,"../operators":33,"./Node":24}],20:[function(require,module,exports){ +'use strict'; + +var format = require('../../utils/string').format; + +var escapeLatex = require('../../utils/latex').escape; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + var getType = load(require('../../function/utils/typeof')); + /** + * A ConstantNode holds a constant value like a number or string. + * + * Usage: + * + * new ConstantNode(2.3) + * new ConstantNode('hello') + * + * @param {*} value Value can be any type (number, BigNumber, string, ...) + * @constructor ConstantNode + * @extends {Node} + */ + + function ConstantNode(value) { + if (!(this instanceof ConstantNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + if (arguments.length === 2) { + // TODO: remove deprecation error some day (created 2018-01-23) + throw new SyntaxError('new ConstantNode(valueStr, valueType) is not supported anymore since math v4.0.0. Use new ConstantNode(value) instead, where value is a non-stringified value.'); + } + + this.value = value; + } + + ConstantNode.prototype = new Node(); + ConstantNode.prototype.type = 'ConstantNode'; + ConstantNode.prototype.isConstantNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + ConstantNode.prototype._compile = function (math, argNames) { + var value = this.value; + return function evalConstantNode() { + return value; + }; + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + ConstantNode.prototype.forEach = function (callback) {} // nothing to do, we don't have childs + + /** + * Create a new ConstantNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node) : Node} callback + * @returns {ConstantNode} Returns a clone of the node + */ + ; + + ConstantNode.prototype.map = function (callback) { + return this.clone(); + }; + /** + * Create a clone of this node, a shallow copy + * @return {ConstantNode} + */ + + + ConstantNode.prototype.clone = function () { + return new ConstantNode(this.value); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + */ + + + ConstantNode.prototype._toString = function (options) { + return format(this.value, options); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + */ + + + ConstantNode.prototype.toHTML = function (options) { + var value = this._toString(options); + + switch (getType(this.value)) { + case 'number': + case 'BigNumber': + case 'Fraction': + return '<span class="math-number">' + value + '</span>'; + + case 'string': + return '<span class="math-string">' + value + '</span>'; + + case 'boolean': + return '<span class="math-boolean">' + value + '</span>'; + + case 'null': + return '<span class="math-null-symbol">' + value + '</span>'; + + case 'undefined': + return '<span class="math-undefined">' + value + '</span>'; + + default: + return '<span class="math-symbol">' + value + '</span>'; + } + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + ConstantNode.prototype.toJSON = function () { + return { + mathjs: 'ConstantNode', + value: this.value + }; + }; + /** + * Instantiate a ConstantNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "SymbolNode", value: 2.3}`, + * where mathjs is optional + * @returns {ConstantNode} + */ + + + ConstantNode.fromJSON = function (json) { + return new ConstantNode(json.value); + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + ConstantNode.prototype._toTex = function (options) { + var value = this._toString(options); + + switch (getType(this.value)) { + case 'string': + return '\\mathtt{' + escapeLatex(value) + '}'; + + case 'number': + case 'BigNumber': + var index = value.toLowerCase().indexOf('e'); + + if (index !== -1) { + return value.substring(0, index) + '\\cdot10^{' + value.substring(index + 1) + '}'; + } + + return value; + + case 'Fraction': + return this.value.toLatex(); + + default: + return value; + } + }; + + return ConstantNode; +} + +exports.name = 'ConstantNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../function/utils/typeof":38,"../../utils/latex":53,"../../utils/string":57,"./Node":24}],21:[function(require,module,exports){ +'use strict'; + +var keywords = require('../keywords'); + +var escape = require('../../utils/string').escape; + +var forEach = require('../../utils/array').forEach; + +var join = require('../../utils/array').join; + +var latex = require('../../utils/latex'); + +var operators = require('../operators'); + +var setSafeProperty = require('../../utils/customs').setSafeProperty; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + /** + * @constructor FunctionAssignmentNode + * @extends {Node} + * Function assignment + * + * @param {string} name Function name + * @param {string[] | Array.<{name: string, type: string}>} params + * Array with function parameter names, or an + * array with objects containing the name + * and type of the parameter + * @param {Node} expr The function expression + */ + + function FunctionAssignmentNode(name, params, expr) { + if (!(this instanceof FunctionAssignmentNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } // validate input + + + if (typeof name !== 'string') throw new TypeError('String expected for parameter "name"'); + if (!Array.isArray(params)) throw new TypeError('Array containing strings or objects expected for parameter "params"'); + if (!type.isNode(expr)) throw new TypeError('Node expected for parameter "expr"'); + if (name in keywords) throw new Error('Illegal function name, "' + name + '" is a reserved keyword'); + this.name = name; + this.params = params.map(function (param) { + return param && param.name || param; + }); + this.types = params.map(function (param) { + return param && param.type || 'any'; + }); + this.expr = expr; + } + + FunctionAssignmentNode.prototype = new Node(); + FunctionAssignmentNode.prototype.type = 'FunctionAssignmentNode'; + FunctionAssignmentNode.prototype.isFunctionAssignmentNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + FunctionAssignmentNode.prototype._compile = function (math, argNames) { + var childArgNames = Object.create(argNames); + forEach(this.params, function (param) { + childArgNames[param] = true; + }); // compile the function expression with the child args + + var evalExpr = this.expr._compile(math, childArgNames); + + var name = this.name; + var params = this.params; + var signature = join(this.types, ','); + var syntax = name + '(' + join(this.params, ', ') + ')'; + return function evalFunctionAssignmentNode(scope, args, context) { + var signatures = {}; + + signatures[signature] = function () { + var childArgs = Object.create(args); + + for (var i = 0; i < params.length; i++) { + childArgs[params[i]] = arguments[i]; + } + + return evalExpr(scope, childArgs, context); + }; + + var fn = typed(name, signatures); + fn.syntax = syntax; + setSafeProperty(scope, name, fn); + return fn; + }; + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + FunctionAssignmentNode.prototype.forEach = function (callback) { + callback(this.expr, 'expr', this); + }; + /** + * Create a new FunctionAssignmentNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {FunctionAssignmentNode} Returns a transformed copy of the node + */ + + + FunctionAssignmentNode.prototype.map = function (callback) { + var expr = this._ifNode(callback(this.expr, 'expr', this)); + + return new FunctionAssignmentNode(this.name, this.params.slice(0), expr); + }; + /** + * Create a clone of this node, a shallow copy + * @return {FunctionAssignmentNode} + */ + + + FunctionAssignmentNode.prototype.clone = function () { + return new FunctionAssignmentNode(this.name, this.params.slice(0), this.expr); + }; + /** + * Is parenthesis needed? + * @param {Node} node + * @param {Object} parenthesis + * @private + */ + + + function needParenthesis(node, parenthesis) { + var precedence = operators.getPrecedence(node, parenthesis); + var exprPrecedence = operators.getPrecedence(node.expr, parenthesis); + return parenthesis === 'all' || exprPrecedence !== null && exprPrecedence <= precedence; + } + /** + * get string representation + * @param {Object} options + * @return {string} str + */ + + + FunctionAssignmentNode.prototype._toString = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var expr = this.expr.toString(options); + + if (needParenthesis(this, parenthesis)) { + expr = '(' + expr + ')'; + } + + return this.name + '(' + this.params.join(', ') + ') = ' + expr; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + FunctionAssignmentNode.prototype.toJSON = function () { + var types = this.types; + return { + mathjs: 'FunctionAssignmentNode', + name: this.name, + params: this.params.map(function (param, index) { + return { + name: param, + type: types[index] + }; + }), + expr: this.expr + }; + }; + /** + * Instantiate an FunctionAssignmentNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "FunctionAssignmentNode", name: ..., params: ..., expr: ...}`, + * where mathjs is optional + * @returns {FunctionAssignmentNode} + */ + + + FunctionAssignmentNode.fromJSON = function (json) { + return new FunctionAssignmentNode(json.name, json.params, json.expr); + }; + /** + * get HTML representation + * @param {Object} options + * @return {string} str + */ + + + FunctionAssignmentNode.prototype.toHTML = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var params = []; + + for (var i = 0; i < this.params.length; i++) { + params.push('<span class="math-symbol math-parameter">' + escape(this.params[i]) + '</span>'); + } + + var expr = this.expr.toHTML(options); + + if (needParenthesis(this, parenthesis)) { + expr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + expr + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + return '<span class="math-function">' + escape(this.name) + '</span>' + '<span class="math-parenthesis math-round-parenthesis">(</span>' + params.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-round-parenthesis">)</span><span class="math-operator math-assignment-operator math-variable-assignment-operator math-binary-operator">=</span>' + expr; + }; + /** + * get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + FunctionAssignmentNode.prototype._toTex = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var expr = this.expr.toTex(options); + + if (needParenthesis(this, parenthesis)) { + expr = "\\left(".concat(expr, "\\right)"); + } + + return '\\mathrm{' + this.name + '}\\left(' + this.params.map(latex.toSymbol).join(',') + '\\right):=' + expr; + }; + + return FunctionAssignmentNode; +} + +exports.name = 'FunctionAssignmentNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../utils/array":46,"../../utils/customs":51,"../../utils/latex":53,"../../utils/string":57,"../keywords":14,"../operators":33,"./Node":24}],22:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +var latex = require('../../utils/latex'); + +var escape = require('../../utils/string').escape; + +var hasOwnProperty = require('../../utils/object').hasOwnProperty; + +var map = require('../../utils/array').map; + +var validateSafeMethod = require('../../utils/customs').validateSafeMethod; + +var getSafeProperty = require('../../utils/customs').getSafeProperty; + +function factory(type, config, load, typed, math) { + var Node = load(require('./Node')); + var SymbolNode = load(require('./SymbolNode')); + /** + * @constructor FunctionNode + * @extends {./Node} + * invoke a list with arguments on a node + * @param {./Node | string} fn Node resolving with a function on which to invoke + * the arguments, typically a SymboNode or AccessorNode + * @param {./Node[]} args + */ + + function FunctionNode(fn, args) { + if (!(this instanceof FunctionNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + if (typeof fn === 'string') { + fn = new SymbolNode(fn); + } // validate input + + + if (!type.isNode(fn)) throw new TypeError('Node expected as parameter "fn"'); + + if (!Array.isArray(args) || !args.every(type.isNode)) { + throw new TypeError('Array containing Nodes expected for parameter "args"'); + } + + this.fn = fn; + this.args = args || []; // readonly property name + + Object.defineProperty(this, 'name', { + get: function () { + return this.fn.name || ''; + }.bind(this), + set: function set() { + throw new Error('Cannot assign a new name, name is read-only'); + } + }); // TODO: deprecated since v3, remove some day + + var deprecated = function deprecated() { + throw new Error('Property `FunctionNode.object` is deprecated, use `FunctionNode.fn` instead'); + }; + + Object.defineProperty(this, 'object', { + get: deprecated, + set: deprecated + }); + } + + FunctionNode.prototype = new Node(); + FunctionNode.prototype.type = 'FunctionNode'; + FunctionNode.prototype.isFunctionNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + FunctionNode.prototype._compile = function (math, argNames) { + if (!(this instanceof FunctionNode)) { + throw new TypeError('No valid FunctionNode'); + } // compile arguments + + + var evalArgs = map(this.args, function (arg) { + return arg._compile(math, argNames); + }); + + if (type.isSymbolNode(this.fn)) { + // we can statically determine whether the function has an rawArgs property + var name = this.fn.name; + var fn = name in math ? getSafeProperty(math, name) : undefined; + var isRaw = typeof fn === 'function' && fn.rawArgs === true; + + if (isRaw) { + // pass unevaluated parameters (nodes) to the function + // "raw" evaluation + var rawArgs = this.args; + return function evalFunctionNode(scope, args, context) { + return (name in scope ? getSafeProperty(scope, name) : fn)(rawArgs, math, _extends({}, scope, args)); + }; + } else { + // "regular" evaluation + if (evalArgs.length === 1) { + var evalArg0 = evalArgs[0]; + return function evalFunctionNode(scope, args, context) { + return (name in scope ? getSafeProperty(scope, name) : fn)(evalArg0(scope, args, context)); + }; + } else if (evalArgs.length === 2) { + var _evalArg = evalArgs[0]; + var evalArg1 = evalArgs[1]; + return function evalFunctionNode(scope, args, context) { + return (name in scope ? getSafeProperty(scope, name) : fn)(_evalArg(scope, args, context), evalArg1(scope, args, context)); + }; + } else { + return function evalFunctionNode(scope, args, context) { + return (name in scope ? getSafeProperty(scope, name) : fn).apply(null, map(evalArgs, function (evalArg) { + return evalArg(scope, args, context); + })); + }; + } + } + } else if (type.isAccessorNode(this.fn) && type.isIndexNode(this.fn.index) && this.fn.index.isObjectProperty()) { + // execute the function with the right context: the object of the AccessorNode + var evalObject = this.fn.object._compile(math, argNames); + + var prop = this.fn.index.getObjectProperty(); + var _rawArgs = this.args; + return function evalFunctionNode(scope, args, context) { + var object = evalObject(scope, args, context); + validateSafeMethod(object, prop); + var isRaw = object[prop] && object[prop].rawArgs; + return isRaw ? object[prop](_rawArgs, math, _extends({}, scope, args)) // "raw" evaluation + : object[prop].apply(object, map(evalArgs, function (evalArg) { + // "regular" evaluation + return evalArg(scope, args, context); + })); + }; + } else { + // node.fn.isAccessorNode && !node.fn.index.isObjectProperty() + // we have to dynamically determine whether the function has a rawArgs property + var evalFn = this.fn._compile(math, argNames); + + var _rawArgs2 = this.args; + return function evalFunctionNode(scope, args, context) { + var fn = evalFn(scope, args, context); + var isRaw = fn && fn.rawArgs; + return isRaw ? fn(_rawArgs2, math, _extends({}, scope, args)) // "raw" evaluation + : fn.apply(fn, map(evalArgs, function (evalArg) { + // "regular" evaluation + return evalArg(scope, args, context); + })); + }; + } + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + FunctionNode.prototype.forEach = function (callback) { + for (var i = 0; i < this.args.length; i++) { + callback(this.args[i], 'args[' + i + ']', this); + } + }; + /** + * Create a new FunctionNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {FunctionNode} Returns a transformed copy of the node + */ + + + FunctionNode.prototype.map = function (callback) { + var fn = this.fn.map(callback); + var args = []; + + for (var i = 0; i < this.args.length; i++) { + args[i] = this._ifNode(callback(this.args[i], 'args[' + i + ']', this)); + } + + return new FunctionNode(fn, args); + }; + /** + * Create a clone of this node, a shallow copy + * @return {FunctionNode} + */ + + + FunctionNode.prototype.clone = function () { + return new FunctionNode(this.fn, this.args.slice(0)); + }; // backup Node's toString function + // @private + + + var nodeToString = FunctionNode.prototype.toString; + /** + * Get string representation. (wrapper function) + * This overrides parts of Node's toString function. + * If callback is an object containing callbacks, it + * calls the correct callback for the current node, + * otherwise it falls back to calling Node's toString + * function. + * + * @param {Object} options + * @return {string} str + * @override + */ + + FunctionNode.prototype.toString = function (options) { + var customString; + var name = this.fn.toString(options); + + if (options && _typeof(options.handler) === 'object' && hasOwnProperty(options.handler, name)) { + // callback is a map of callback functions + customString = options.handler[name](this, options); + } + + if (typeof customString !== 'undefined') { + return customString; + } // fall back to Node's toString + + + return nodeToString.call(this, options); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + */ + + + FunctionNode.prototype._toString = function (options) { + var args = this.args.map(function (arg) { + return arg.toString(options); + }); + var fn = type.isFunctionAssignmentNode(this.fn) ? '(' + this.fn.toString(options) + ')' : this.fn.toString(options); // format the arguments like "add(2, 4.2)" + + return fn + '(' + args.join(', ') + ')'; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + FunctionNode.prototype.toJSON = function () { + return { + mathjs: 'FunctionNode', + fn: this.fn, + args: this.args + }; + }; + /** + * Instantiate an AssignmentNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "FunctionNode", fn: ..., args: ...}`, + * where mathjs is optional + * @returns {FunctionNode} + */ + + + FunctionNode.fromJSON = function (json) { + return new FunctionNode(json.fn, json.args); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + */ + + + FunctionNode.prototype.toHTML = function (options) { + var args = this.args.map(function (arg) { + return arg.toHTML(options); + }); // format the arguments like "add(2, 4.2)" + + return '<span class="math-function">' + escape(this.fn) + '</span><span class="math-paranthesis math-round-parenthesis">(</span>' + args.join('<span class="math-separator">,</span>') + '<span class="math-paranthesis math-round-parenthesis">)</span>'; + }; + /* + * Expand a LaTeX template + * + * @param {string} template + * @param {Node} node + * @param {Object} options + * @private + **/ + + + function expandTemplate(template, node, options) { + var latex = ''; // Match everything of the form ${identifier} or ${identifier[2]} or $$ + // while submatching identifier and 2 (in the second case) + + var regex = new RegExp('\\$(?:\\{([a-z_][a-z_0-9]*)(?:\\[([0-9]+)\\])?\\}|\\$)', 'ig'); + var inputPos = 0; // position in the input string + + var match; + + while ((match = regex.exec(template)) !== null) { + // go through all matches + // add everything in front of the match to the LaTeX string + latex += template.substring(inputPos, match.index); + inputPos = match.index; + + if (match[0] === '$$') { + // escaped dollar sign + latex += '$'; + inputPos++; + } else { + // template parameter + inputPos += match[0].length; + var property = node[match[1]]; + + if (!property) { + throw new ReferenceError('Template: Property ' + match[1] + ' does not exist.'); + } + + if (match[2] === undefined) { + // no square brackets + switch (_typeof(property)) { + case 'string': + latex += property; + break; + + case 'object': + if (type.isNode(property)) { + latex += property.toTex(options); + } else if (Array.isArray(property)) { + // make array of Nodes into comma separated list + latex += property.map(function (arg, index) { + if (type.isNode(arg)) { + return arg.toTex(options); + } + + throw new TypeError('Template: ' + match[1] + '[' + index + '] is not a Node.'); + }).join(','); + } else { + throw new TypeError('Template: ' + match[1] + ' has to be a Node, String or array of Nodes'); + } + + break; + + default: + throw new TypeError('Template: ' + match[1] + ' has to be a Node, String or array of Nodes'); + } + } else { + // with square brackets + if (type.isNode(property[match[2]] && property[match[2]])) { + latex += property[match[2]].toTex(options); + } else { + throw new TypeError('Template: ' + match[1] + '[' + match[2] + '] is not a Node.'); + } + } + } + } + + latex += template.slice(inputPos); // append rest of the template + + return latex; + } // backup Node's toTex function + // @private + + + var nodeToTex = FunctionNode.prototype.toTex; + /** + * Get LaTeX representation. (wrapper function) + * This overrides parts of Node's toTex function. + * If callback is an object containing callbacks, it + * calls the correct callback for the current node, + * otherwise it falls back to calling Node's toTex + * function. + * + * @param {Object} options + * @return {string} + */ + + FunctionNode.prototype.toTex = function (options) { + var customTex; + + if (options && _typeof(options.handler) === 'object' && hasOwnProperty(options.handler, this.name)) { + // callback is a map of callback functions + customTex = options.handler[this.name](this, options); + } + + if (typeof customTex !== 'undefined') { + return customTex; + } // fall back to Node's toTex + + + return nodeToTex.call(this, options); + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + FunctionNode.prototype._toTex = function (options) { + var args = this.args.map(function (arg) { + // get LaTeX of the arguments + return arg.toTex(options); + }); + var latexConverter; + + if (math[this.name] && (typeof math[this.name].toTex === 'function' || _typeof(math[this.name].toTex) === 'object' || typeof math[this.name].toTex === 'string')) { + // .toTex is a callback function + latexConverter = math[this.name].toTex; + } + + var customToTex; + + switch (_typeof(latexConverter)) { + case 'function': + // a callback function + customToTex = latexConverter(this, options); + break; + + case 'string': + // a template string + customToTex = expandTemplate(latexConverter, this, options); + break; + + case 'object': + // an object with different "converters" for different numbers of arguments + switch (_typeof(latexConverter[args.length])) { + case 'function': + customToTex = latexConverter[args.length](this, options); + break; + + case 'string': + customToTex = expandTemplate(latexConverter[args.length], this, options); + break; + } + + } + + if (typeof customToTex !== 'undefined') { + return customToTex; + } + + return expandTemplate(latex.defaultTemplate, this, options); + }; + /** + * Get identifier. + * @return {string} + */ + + + FunctionNode.prototype.getIdentifier = function () { + return this.type + ':' + this.name; + }; + + return FunctionNode; +} + +exports.name = 'FunctionNode'; +exports.path = 'expression.node'; +exports.math = true; // request access to the math namespace as 5th argument of the factory function + +exports.factory = factory; +},{"../../utils/array":46,"../../utils/customs":51,"../../utils/latex":53,"../../utils/object":55,"../../utils/string":57,"./Node":24,"./SymbolNode":30}],23:[function(require,module,exports){ +'use strict'; + +var map = require('../../utils/array').map; + +var escape = require('../../utils/string').escape; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + var Range = load(require('../../type/matrix/Range')); + var isArray = Array.isArray; + /** + * @constructor IndexNode + * @extends Node + * + * Describes a subset of a matrix or an object property. + * Cannot be used on its own, needs to be used within an AccessorNode or + * AssignmentNode. + * + * @param {Node[]} dimensions + * @param {boolean} [dotNotation=false] Optional property describing whether + * this index was written using dot + * notation like `a.b`, or using bracket + * notation like `a["b"]` (default). + * Used to stringify an IndexNode. + */ + + function IndexNode(dimensions, dotNotation) { + if (!(this instanceof IndexNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this.dimensions = dimensions; + this.dotNotation = dotNotation || false; // validate input + + if (!isArray(dimensions) || !dimensions.every(type.isNode)) { + throw new TypeError('Array containing Nodes expected for parameter "dimensions"'); + } + + if (this.dotNotation && !this.isObjectProperty()) { + throw new Error('dotNotation only applicable for object properties'); + } // TODO: deprecated since v3, remove some day + + + var deprecated = function deprecated() { + throw new Error('Property `IndexNode.object` is deprecated, use `IndexNode.fn` instead'); + }; + + Object.defineProperty(this, 'object', { + get: deprecated, + set: deprecated + }); + } + + IndexNode.prototype = new Node(); + IndexNode.prototype.type = 'IndexNode'; + IndexNode.prototype.isIndexNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + IndexNode.prototype._compile = function (math, argNames) { + // TODO: implement support for bignumber (currently bignumbers are silently + // reduced to numbers when changing the value to zero-based) + // TODO: Optimization: when the range values are ConstantNodes, + // we can beforehand resolve the zero-based value + // optimization for a simple object property + var evalDimensions = map(this.dimensions, function (range, i) { + if (type.isRangeNode(range)) { + if (range.needsEnd()) { + // create a range containing end (like '4:end') + var childArgNames = Object.create(argNames); + childArgNames['end'] = true; + + var evalStart = range.start._compile(math, childArgNames); + + var evalEnd = range.end._compile(math, childArgNames); + + var evalStep = range.step ? range.step._compile(math, childArgNames) : function () { + return 1; + }; + return function evalDimension(scope, args, context) { + var size = math.size(context).valueOf(); + var childArgs = Object.create(args); + childArgs['end'] = size[i]; + return createRange(evalStart(scope, childArgs, context), evalEnd(scope, childArgs, context), evalStep(scope, childArgs, context)); + }; + } else { + // create range + var _evalStart = range.start._compile(math, argNames); + + var _evalEnd = range.end._compile(math, argNames); + + var _evalStep = range.step ? range.step._compile(math, argNames) : function () { + return 1; + }; + + return function evalDimension(scope, args, context) { + return createRange(_evalStart(scope, args, context), _evalEnd(scope, args, context), _evalStep(scope, args, context)); + }; + } + } else if (type.isSymbolNode(range) && range.name === 'end') { + // SymbolNode 'end' + var _childArgNames = Object.create(argNames); + + _childArgNames['end'] = true; + + var evalRange = range._compile(math, _childArgNames); + + return function evalDimension(scope, args, context) { + var size = math.size(context).valueOf(); + var childArgs = Object.create(args); + childArgs['end'] = size[i]; + return evalRange(scope, childArgs, context); + }; + } else { + // ConstantNode + var _evalRange = range._compile(math, argNames); + + return function evalDimension(scope, args, context) { + return _evalRange(scope, args, context); + }; + } + }); + return function evalIndexNode(scope, args, context) { + var dimensions = map(evalDimensions, function (evalDimension) { + return evalDimension(scope, args, context); + }); + return math.index.apply(math, dimensions); + }; + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + IndexNode.prototype.forEach = function (callback) { + for (var i = 0; i < this.dimensions.length; i++) { + callback(this.dimensions[i], 'dimensions[' + i + ']', this); + } + }; + /** + * Create a new IndexNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {IndexNode} Returns a transformed copy of the node + */ + + + IndexNode.prototype.map = function (callback) { + var dimensions = []; + + for (var i = 0; i < this.dimensions.length; i++) { + dimensions[i] = this._ifNode(callback(this.dimensions[i], 'dimensions[' + i + ']', this)); + } + + return new IndexNode(dimensions); + }; + /** + * Create a clone of this node, a shallow copy + * @return {IndexNode} + */ + + + IndexNode.prototype.clone = function () { + return new IndexNode(this.dimensions.slice(0)); + }; + /** + * Test whether this IndexNode contains a single property name + * @return {boolean} + */ + + + IndexNode.prototype.isObjectProperty = function () { + return this.dimensions.length === 1 && type.isConstantNode(this.dimensions[0]) && typeof this.dimensions[0].value === 'string'; + }; + /** + * Returns the property name if IndexNode contains a property. + * If not, returns null. + * @return {string | null} + */ + + + IndexNode.prototype.getObjectProperty = function () { + return this.isObjectProperty() ? this.dimensions[0].value : null; + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + */ + + + IndexNode.prototype._toString = function (options) { + // format the parameters like "[1, 0:5]" + return this.dotNotation ? '.' + this.getObjectProperty() : '[' + this.dimensions.join(', ') + ']'; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + IndexNode.prototype.toJSON = function () { + return { + mathjs: 'IndexNode', + dimensions: this.dimensions, + dotNotation: this.dotNotation + }; + }; + /** + * Instantiate an IndexNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "IndexNode", dimensions: [...], dotNotation: false}`, + * where mathjs is optional + * @returns {IndexNode} + */ + + + IndexNode.fromJSON = function (json) { + return new IndexNode(json.dimensions, json.dotNotation); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + */ + + + IndexNode.prototype.toHTML = function (options) { + // format the parameters like "[1, 0:5]" + var dimensions = []; + + for (var i = 0; i < this.dimensions.length; i++) { + dimensions[i] = this.dimensions[i].toHTML(); + } + + if (this.dotNotation) { + return '<span class="math-operator math-accessor-operator">.</span>' + '<span class="math-symbol math-property">' + escape(this.getObjectProperty()) + '</span>'; + } else { + return '<span class="math-parenthesis math-square-parenthesis">[</span>' + dimensions.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-square-parenthesis">]</span>'; + } + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + IndexNode.prototype._toTex = function (options) { + var dimensions = this.dimensions.map(function (range) { + return range.toTex(options); + }); + return this.dotNotation ? '.' + this.getObjectProperty() + '' : '_{' + dimensions.join(',') + '}'; + }; // helper function to create a Range from start, step and end + + + function createRange(start, end, step) { + return new Range(type.isBigNumber(start) ? start.toNumber() : start, type.isBigNumber(end) ? end.toNumber() : end, type.isBigNumber(step) ? step.toNumber() : step); + } + + return IndexNode; +} + +exports.name = 'IndexNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../type/matrix/Range":41,"../../utils/array":46,"../../utils/string":57,"./Node":24}],24:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var keywords = require('../keywords'); + +var deepEqual = require('../../utils/object').deepEqual; + +var hasOwnProperty = require('../../utils/object').hasOwnProperty; + +function factory(type, config, load, typed, math) { + /** + * Node + */ + function Node() { + if (!(this instanceof Node)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + } + /** + * Evaluate the node + * @param {Object} [scope] Scope to read/write variables + * @return {*} Returns the result + */ + + + Node.prototype.eval = function (scope) { + return this.compile().eval(scope); + }; + + Node.prototype.type = 'Node'; + Node.prototype.isNode = true; + Node.prototype.comment = ''; + /** + * Compile the node into an optimized, evauatable JavaScript function + * @return {{eval: function([Object])}} expr Returns an object with a function 'eval', + * which can be invoked as expr.eval([scope: Object]), + * where scope is an optional object with + * variables. + */ + + Node.prototype.compile = function () { + var expr = this._compile(math.expression.mathWithTransform, {}); + + var args = {}; + var context = null; + return { + eval: function evalNode(scope) { + var s = scope || {}; + + _validateScope(s); + + return expr(s, args, context); + } + }; + }; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + + Node.prototype._compile = function (math, argNames) { + throw new Error('Method _compile should be implemented by type ' + this.type); + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + Node.prototype.forEach = function (callback) { + // must be implemented by each of the Node implementations + throw new Error('Cannot run forEach on a Node interface'); + }; + /** + * Create a new Node having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {OperatorNode} Returns a transformed copy of the node + */ + + + Node.prototype.map = function (callback) { + // must be implemented by each of the Node implementations + throw new Error('Cannot run map on a Node interface'); + }; + /** + * Validate whether an object is a Node, for use with map + * @param {Node} node + * @returns {Node} Returns the input if it's a node, else throws an Error + * @protected + */ + + + Node.prototype._ifNode = function (node) { + if (!type.isNode(node)) { + throw new TypeError('Callback function must return a Node'); + } + + return node; + }; + /** + * Recursively traverse all nodes in a node tree. Executes given callback for + * this node and each of its child nodes. + * @param {function(node: Node, path: string, parent: Node)} callback + * A callback called for every node in the node tree. + */ + + + Node.prototype.traverse = function (callback) { + // execute callback for itself + callback(this, null, null); // eslint-disable-line standard/no-callback-literal + // recursively traverse over all childs of a node + + function _traverse(node, callback) { + node.forEach(function (child, path, parent) { + callback(child, path, parent); + + _traverse(child, callback); + }); + } + + _traverse(this, callback); + }; + /** + * Recursively transform a node tree via a transform function. + * + * For example, to replace all nodes of type SymbolNode having name 'x' with a + * ConstantNode with value 2: + * + * const res = Node.transform(function (node, path, parent) { + * if (node && node.isSymbolNode) && (node.name === 'x')) { + * return new ConstantNode(2) + * } + * else { + * return node + * } + * }) + * + * @param {function(node: Node, path: string, parent: Node) : Node} callback + * A mapping function accepting a node, and returning + * a replacement for the node or the original node. + * Signature: callback(node: Node, index: string, parent: Node) : Node + * @return {Node} Returns the original node or its replacement + */ + + + Node.prototype.transform = function (callback) { + // traverse over all childs + function _transform(node, callback) { + return node.map(function (child, path, parent) { + var replacement = callback(child, path, parent); + return _transform(replacement, callback); + }); + } + + var replacement = callback(this, null, null); // eslint-disable-line standard/no-callback-literal + + return _transform(replacement, callback); + }; + /** + * Find any node in the node tree matching given filter function. For example, to + * find all nodes of type SymbolNode having name 'x': + * + * const results = Node.filter(function (node) { + * return (node && node.isSymbolNode) && (node.name === 'x') + * }) + * + * @param {function(node: Node, path: string, parent: Node) : Node} callback + * A test function returning true when a node matches, and false + * otherwise. Function signature: + * callback(node: Node, index: string, parent: Node) : boolean + * @return {Node[]} nodes An array with nodes matching given filter criteria + */ + + + Node.prototype.filter = function (callback) { + var nodes = []; + this.traverse(function (node, path, parent) { + if (callback(node, path, parent)) { + nodes.push(node); + } + }); + return nodes; + }; // TODO: deprecated since version 1.1.0, remove this some day + + + Node.prototype.find = function () { + throw new Error('Function Node.find is deprecated. Use Node.filter instead.'); + }; // TODO: deprecated since version 1.1.0, remove this some day + + + Node.prototype.match = function () { + throw new Error('Function Node.match is deprecated. See functions Node.filter, Node.transform, Node.traverse.'); + }; + /** + * Create a shallow clone of this node + * @return {Node} + */ + + + Node.prototype.clone = function () { + // must be implemented by each of the Node implementations + throw new Error('Cannot clone a Node interface'); + }; + /** + * Create a deep clone of this node + * @return {Node} + */ + + + Node.prototype.cloneDeep = function () { + return this.map(function (node) { + return node.cloneDeep(); + }); + }; + /** + * Deep compare this node with another node. + * @param {Node} other + * @return {boolean} Returns true when both nodes are of the same type and + * contain the same values (as do their childs) + */ + + + Node.prototype.equals = function (other) { + return other ? deepEqual(this, other) : false; + }; + /** + * Get string representation. (wrapper function) + * + * This function can get an object of the following form: + * { + * handler: //This can be a callback function of the form + * // "function callback(node, options)"or + * // a map that maps function names (used in FunctionNodes) + * // to callbacks + * parenthesis: "keep" //the parenthesis option (This is optional) + * } + * + * @param {Object} [options] + * @return {string} + */ + + + Node.prototype.toString = function (options) { + var customString; + + if (options && _typeof(options) === 'object') { + switch (_typeof(options.handler)) { + case 'object': + case 'undefined': + break; + + case 'function': + customString = options.handler(this, options); + break; + + default: + throw new TypeError('Object or function expected as callback'); + } + } + + if (typeof customString !== 'undefined') { + return customString; + } + + return this._toString(options); + }; + /** + * Get a JSON representation of the node + * Both .toJSON() and the static .fromJSON(json) should be implemented by all + * implementations of Node + * @returns {Object} + */ + + + Node.prototype.toJSON = function () { + throw new Error('Cannot serialize object: toJSON not implemented by ' + this.type); + }; + /** + * Get HTML representation. (wrapper function) + * + * This function can get an object of the following form: + * { + * handler: //This can be a callback function of the form + * // "function callback(node, options)" or + * // a map that maps function names (used in FunctionNodes) + * // to callbacks + * parenthesis: "keep" //the parenthesis option (This is optional) + * } + * + * @param {Object} [options] + * @return {string} + */ + + + Node.prototype.toHTML = function (options) { + var customString; + + if (options && _typeof(options) === 'object') { + switch (_typeof(options.handler)) { + case 'object': + case 'undefined': + break; + + case 'function': + customString = options.handler(this, options); + break; + + default: + throw new TypeError('Object or function expected as callback'); + } + } + + if (typeof customString !== 'undefined') { + return customString; + } + + return this.toHTML(options); + }; + /** + * Internal function to generate the string output. + * This has to be implemented by every Node + * + * @throws {Error} + */ + + + Node.prototype._toString = function () { + // must be implemented by each of the Node implementations + throw new Error('_toString not implemented for ' + this.type); + }; + /** + * Get LaTeX representation. (wrapper function) + * + * This function can get an object of the following form: + * { + * handler: //This can be a callback function of the form + * // "function callback(node, options)"or + * // a map that maps function names (used in FunctionNodes) + * // to callbacks + * parenthesis: "keep" //the parenthesis option (This is optional) + * } + * + * @param {Object} [options] + * @return {string} + */ + + + Node.prototype.toTex = function (options) { + var customTex; + + if (options && _typeof(options) === 'object') { + switch (_typeof(options.handler)) { + case 'object': + case 'undefined': + break; + + case 'function': + customTex = options.handler(this, options); + break; + + default: + throw new TypeError('Object or function expected as callback'); + } + } + + if (typeof customTex !== 'undefined') { + return customTex; + } + + return this._toTex(options); + }; + /** + * Internal function to generate the LaTeX output. + * This has to be implemented by every Node + * + * @param {Object} [options] + * @throws {Error} + */ + + + Node.prototype._toTex = function (options) { + // must be implemented by each of the Node implementations + throw new Error('_toTex not implemented for ' + this.type); + }; + /** + * Get identifier. + * @return {string} + */ + + + Node.prototype.getIdentifier = function () { + return this.type; + }; + /** + * Get the content of the current Node. + * @return {Node} node + **/ + + + Node.prototype.getContent = function () { + return this; + }; + /** + * Validate the symbol names of a scope. + * Throws an error when the scope contains an illegal symbol. + * @param {Object} scope + */ + + + function _validateScope(scope) { + for (var symbol in scope) { + if (hasOwnProperty(scope, symbol)) { + if (symbol in keywords) { + throw new Error('Scope contains an illegal symbol, "' + symbol + '" is a reserved keyword'); + } + } + } + } + + return Node; +} + +exports.name = 'Node'; +exports.path = 'expression.node'; +exports.math = true; // request access to the math namespace as 5th argument of the factory function + +exports.factory = factory; +},{"../../utils/object":55,"../keywords":14}],25:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var stringify = require('../../utils/string').stringify; + +var escape = require('../../utils/string').escape; + +var isSafeProperty = require('../../utils/customs').isSafeProperty; + +var hasOwnProperty = require('../../utils/object').hasOwnProperty; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + /** + * @constructor ObjectNode + * @extends {Node} + * Holds an object with keys/values + * @param {Object.<string, Node>} [properties] object with key/value pairs + */ + + function ObjectNode(properties) { + if (!(this instanceof ObjectNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this.properties = properties || {}; // validate input + + if (properties) { + if (!(_typeof(properties) === 'object') || !Object.keys(properties).every(function (key) { + return type.isNode(properties[key]); + })) { + throw new TypeError('Object containing Nodes expected'); + } + } + } + + ObjectNode.prototype = new Node(); + ObjectNode.prototype.type = 'ObjectNode'; + ObjectNode.prototype.isObjectNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + ObjectNode.prototype._compile = function (math, argNames) { + var evalEntries = {}; + + for (var key in this.properties) { + if (hasOwnProperty(this.properties, key)) { + // we stringify/parse the key here to resolve unicode characters, + // so you cannot create a key like {"co\\u006Estructor": null} + var stringifiedKey = stringify(key); + var parsedKey = JSON.parse(stringifiedKey); + + if (!isSafeProperty(this.properties, parsedKey)) { + throw new Error('No access to property "' + parsedKey + '"'); + } + + evalEntries[parsedKey] = this.properties[key]._compile(math, argNames); + } + } + + return function evalObjectNode(scope, args, context) { + var obj = {}; + + for (var _key in evalEntries) { + if (hasOwnProperty(evalEntries, _key)) { + obj[_key] = evalEntries[_key](scope, args, context); + } + } + + return obj; + }; + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + ObjectNode.prototype.forEach = function (callback) { + for (var key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + callback(this.properties[key], 'properties[' + stringify(key) + ']', this); + } + } + }; + /** + * Create a new ObjectNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {ObjectNode} Returns a transformed copy of the node + */ + + + ObjectNode.prototype.map = function (callback) { + var properties = {}; + + for (var key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + properties[key] = this._ifNode(callback(this.properties[key], 'properties[' + stringify(key) + ']', this)); + } + } + + return new ObjectNode(properties); + }; + /** + * Create a clone of this node, a shallow copy + * @return {ObjectNode} + */ + + + ObjectNode.prototype.clone = function () { + var properties = {}; + + for (var key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + properties[key] = this.properties[key]; + } + } + + return new ObjectNode(properties); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + * @override + */ + + + ObjectNode.prototype._toString = function (options) { + var entries = []; + + for (var key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + entries.push(stringify(key) + ': ' + this.properties[key].toString(options)); + } + } + + return '{' + entries.join(', ') + '}'; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + ObjectNode.prototype.toJSON = function () { + return { + mathjs: 'ObjectNode', + properties: this.properties + }; + }; + /** + * Instantiate an OperatorNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "ObjectNode", "properties": {...}}`, + * where mathjs is optional + * @returns {ObjectNode} + */ + + + ObjectNode.fromJSON = function (json) { + return new ObjectNode(json.properties); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + * @override + */ + + + ObjectNode.prototype.toHTML = function (options) { + var entries = []; + + for (var key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + entries.push('<span class="math-symbol math-property">' + escape(key) + '</span>' + '<span class="math-operator math-assignment-operator math-property-assignment-operator math-binary-operator">:</span>' + this.properties[key].toHTML(options)); + } + } + + return '<span class="math-parenthesis math-curly-parenthesis">{</span>' + entries.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-curly-parenthesis">}</span>'; + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + ObjectNode.prototype._toTex = function (options) { + var entries = []; + + for (var key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + entries.push('\\mathbf{' + key + ':} & ' + this.properties[key].toTex(options) + '\\\\'); + } + } + + return "\\left\\{\\begin{array}{ll}".concat(entries.join('\n'), "\\end{array}\\right\\}"); + }; + + return ObjectNode; +} + +exports.name = 'ObjectNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../utils/customs":51,"../../utils/object":55,"../../utils/string":57,"./Node":24}],26:[function(require,module,exports){ +'use strict'; + +var latex = require('../../utils/latex'); + +var map = require('../../utils/array').map; + +var escape = require('../../utils/string').escape; + +var isSafeMethod = require('../../utils/customs').isSafeMethod; + +var getSafeProperty = require('../../utils/customs').getSafeProperty; + +var operators = require('../operators'); + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + /** + * @constructor OperatorNode + * @extends {Node} + * An operator with two arguments, like 2+3 + * + * @param {string} op Operator name, for example '+' + * @param {string} fn Function name, for example 'add' + * @param {Node[]} args Operator arguments + * @param {boolean} [implicit] Is this an implicit multiplication? + */ + + function OperatorNode(op, fn, args, implicit) { + if (!(this instanceof OperatorNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } // validate input + + + if (typeof op !== 'string') { + throw new TypeError('string expected for parameter "op"'); + } + + if (typeof fn !== 'string') { + throw new TypeError('string expected for parameter "fn"'); + } + + if (!Array.isArray(args) || !args.every(type.isNode)) { + throw new TypeError('Array containing Nodes expected for parameter "args"'); + } + + this.implicit = implicit === true; + this.op = op; + this.fn = fn; + this.args = args || []; + } + + OperatorNode.prototype = new Node(); + OperatorNode.prototype.type = 'OperatorNode'; + OperatorNode.prototype.isOperatorNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + OperatorNode.prototype._compile = function (math, argNames) { + // validate fn + if (typeof this.fn !== 'string' || !isSafeMethod(math, this.fn)) { + if (!math[this.fn]) { + throw new Error('Function ' + this.fn + ' missing in provided namespace "math"'); + } else { + throw new Error('No access to function "' + this.fn + '"'); + } + } + + var fn = getSafeProperty(math, this.fn); + var evalArgs = map(this.args, function (arg) { + return arg._compile(math, argNames); + }); + + if (evalArgs.length === 1) { + var evalArg0 = evalArgs[0]; + return function evalOperatorNode(scope, args, context) { + return fn(evalArg0(scope, args, context)); + }; + } else if (evalArgs.length === 2) { + var _evalArg = evalArgs[0]; + var evalArg1 = evalArgs[1]; + return function evalOperatorNode(scope, args, context) { + return fn(_evalArg(scope, args, context), evalArg1(scope, args, context)); + }; + } else { + return function evalOperatorNode(scope, args, context) { + return fn.apply(null, map(evalArgs, function (evalArg) { + return evalArg(scope, args, context); + })); + }; + } + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + OperatorNode.prototype.forEach = function (callback) { + for (var i = 0; i < this.args.length; i++) { + callback(this.args[i], 'args[' + i + ']', this); + } + }; + /** + * Create a new OperatorNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {OperatorNode} Returns a transformed copy of the node + */ + + + OperatorNode.prototype.map = function (callback) { + var args = []; + + for (var i = 0; i < this.args.length; i++) { + args[i] = this._ifNode(callback(this.args[i], 'args[' + i + ']', this)); + } + + return new OperatorNode(this.op, this.fn, args, this.implicit); + }; + /** + * Create a clone of this node, a shallow copy + * @return {OperatorNode} + */ + + + OperatorNode.prototype.clone = function () { + return new OperatorNode(this.op, this.fn, this.args.slice(0), this.implicit); + }; + /** + * Check whether this is an unary OperatorNode: + * has exactly one argument, like `-a`. + * @return {boolean} Returns true when an unary operator node, false otherwise. + */ + + + OperatorNode.prototype.isUnary = function () { + return this.args.length === 1; + }; + /** + * Check whether this is a binary OperatorNode: + * has exactly two arguments, like `a + b`. + * @return {boolean} Returns true when a binary operator node, false otherwise. + */ + + + OperatorNode.prototype.isBinary = function () { + return this.args.length === 2; + }; + /** + * Calculate which parentheses are necessary. Gets an OperatorNode + * (which is the root of the tree) and an Array of Nodes + * (this.args) and returns an array where 'true' means that an argument + * has to be enclosed in parentheses whereas 'false' means the opposite. + * + * @param {OperatorNode} root + * @param {string} parenthesis + * @param {Node[]} args + * @param {boolean} latex + * @return {boolean[]} + * @private + */ + + + function calculateNecessaryParentheses(root, parenthesis, implicit, args, latex) { + // precedence of the root OperatorNode + var precedence = operators.getPrecedence(root, parenthesis); + var associativity = operators.getAssociativity(root, parenthesis); + + if (parenthesis === 'all' || args.length > 2 && root.getIdentifier() !== 'OperatorNode:add' && root.getIdentifier() !== 'OperatorNode:multiply') { + var parens = args.map(function (arg) { + switch (arg.getContent().type) { + // Nodes that don't need extra parentheses + case 'ArrayNode': + case 'ConstantNode': + case 'SymbolNode': + case 'ParenthesisNode': + return false; + + default: + return true; + } + }); + return parens; + } + + var result; + + switch (args.length) { + case 0: + result = []; + break; + + case 1: + // unary operators + // precedence of the operand + var operandPrecedence = operators.getPrecedence(args[0], parenthesis); // handle special cases for LaTeX, where some of the parentheses aren't needed + + if (latex && operandPrecedence !== null) { + var operandIdentifier; + var rootIdentifier; + + if (parenthesis === 'keep') { + operandIdentifier = args[0].getIdentifier(); + rootIdentifier = root.getIdentifier(); + } else { + // Ignore Parenthesis Nodes when not in 'keep' mode + operandIdentifier = args[0].getContent().getIdentifier(); + rootIdentifier = root.getContent().getIdentifier(); + } + + if (operators.properties[precedence][rootIdentifier].latexLeftParens === false) { + result = [false]; + break; + } + + if (operators.properties[operandPrecedence][operandIdentifier].latexParens === false) { + result = [false]; + break; + } + } + + if (operandPrecedence === null) { + // if the operand has no defined precedence, no parens are needed + result = [false]; + break; + } + + if (operandPrecedence <= precedence) { + // if the operands precedence is lower, parens are needed + result = [true]; + break; + } // otherwise, no parens needed + + + result = [false]; + break; + + case 2: + // binary operators + var lhsParens; // left hand side needs parenthesis? + // precedence of the left hand side + + var lhsPrecedence = operators.getPrecedence(args[0], parenthesis); // is the root node associative with the left hand side + + var assocWithLhs = operators.isAssociativeWith(root, args[0], parenthesis); + + if (lhsPrecedence === null) { + // if the left hand side has no defined precedence, no parens are needed + // FunctionNode for example + lhsParens = false; + } else if (lhsPrecedence === precedence && associativity === 'right' && !assocWithLhs) { + // In case of equal precedence, if the root node is left associative + // parens are **never** necessary for the left hand side. + // If it is right associative however, parens are necessary + // if the root node isn't associative with the left hand side + lhsParens = true; + } else if (lhsPrecedence < precedence) { + lhsParens = true; + } else { + lhsParens = false; + } + + var rhsParens; // right hand side needs parenthesis? + // precedence of the right hand side + + var rhsPrecedence = operators.getPrecedence(args[1], parenthesis); // is the root node associative with the right hand side? + + var assocWithRhs = operators.isAssociativeWith(root, args[1], parenthesis); + + if (rhsPrecedence === null) { + // if the right hand side has no defined precedence, no parens are needed + // FunctionNode for example + rhsParens = false; + } else if (rhsPrecedence === precedence && associativity === 'left' && !assocWithRhs) { + // In case of equal precedence, if the root node is right associative + // parens are **never** necessary for the right hand side. + // If it is left associative however, parens are necessary + // if the root node isn't associative with the right hand side + rhsParens = true; + } else if (rhsPrecedence < precedence) { + rhsParens = true; + } else { + rhsParens = false; + } // handle special cases for LaTeX, where some of the parentheses aren't needed + + + if (latex) { + var _rootIdentifier; + + var lhsIdentifier; + var rhsIdentifier; + + if (parenthesis === 'keep') { + _rootIdentifier = root.getIdentifier(); + lhsIdentifier = root.args[0].getIdentifier(); + rhsIdentifier = root.args[1].getIdentifier(); + } else { + // Ignore ParenthesisNodes when not in 'keep' mode + _rootIdentifier = root.getContent().getIdentifier(); + lhsIdentifier = root.args[0].getContent().getIdentifier(); + rhsIdentifier = root.args[1].getContent().getIdentifier(); + } + + if (lhsPrecedence !== null) { + if (operators.properties[precedence][_rootIdentifier].latexLeftParens === false) { + lhsParens = false; + } + + if (operators.properties[lhsPrecedence][lhsIdentifier].latexParens === false) { + lhsParens = false; + } + } + + if (rhsPrecedence !== null) { + if (operators.properties[precedence][_rootIdentifier].latexRightParens === false) { + rhsParens = false; + } + + if (operators.properties[rhsPrecedence][rhsIdentifier].latexParens === false) { + rhsParens = false; + } + } + } + + result = [lhsParens, rhsParens]; + break; + + default: + if (root.getIdentifier() === 'OperatorNode:add' || root.getIdentifier() === 'OperatorNode:multiply') { + result = args.map(function (arg) { + var argPrecedence = operators.getPrecedence(arg, parenthesis); + var assocWithArg = operators.isAssociativeWith(root, arg, parenthesis); + var argAssociativity = operators.getAssociativity(arg, parenthesis); + + if (argPrecedence === null) { + // if the argument has no defined precedence, no parens are needed + return false; + } else if (precedence === argPrecedence && associativity === argAssociativity && !assocWithArg) { + return true; + } else if (argPrecedence < precedence) { + return true; + } + + return false; + }); + } + + break; + } // handles an edge case of 'auto' parentheses with implicit multiplication of ConstantNode + // In that case print parentheses for ParenthesisNodes even though they normally wouldn't be + // printed. + + + if (args.length >= 2 && root.getIdentifier() === 'OperatorNode:multiply' && root.implicit && parenthesis === 'auto' && implicit === 'hide') { + result = args.map(function (arg, index) { + var isParenthesisNode = arg.getIdentifier() === 'ParenthesisNode'; + + if (result[index] || isParenthesisNode) { + // put in parenthesis? + return true; + } + + return false; + }); + } + + return result; + } + /** + * Get string representation. + * @param {Object} options + * @return {string} str + */ + + + OperatorNode.prototype._toString = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var implicit = options && options.implicit ? options.implicit : 'hide'; + var args = this.args; + var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, false); + + if (args.length === 1) { + // unary operators + var assoc = operators.getAssociativity(this, parenthesis); + var operand = args[0].toString(options); + + if (parens[0]) { + operand = '(' + operand + ')'; + } + + if (assoc === 'right') { + // prefix operator + return this.op + operand; + } else if (assoc === 'left') { + // postfix + return operand + this.op; + } // fall back to postfix + + + return operand + this.op; + } else if (args.length === 2) { + var lhs = args[0].toString(options); // left hand side + + var rhs = args[1].toString(options); // right hand side + + if (parens[0]) { + // left hand side in parenthesis? + lhs = '(' + lhs + ')'; + } + + if (parens[1]) { + // right hand side in parenthesis? + rhs = '(' + rhs + ')'; + } + + if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') { + return lhs + ' ' + rhs; + } + + return lhs + ' ' + this.op + ' ' + rhs; + } else if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) { + var stringifiedArgs = args.map(function (arg, index) { + arg = arg.toString(options); + + if (parens[index]) { + // put in parenthesis? + arg = '(' + arg + ')'; + } + + return arg; + }); + + if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') { + return stringifiedArgs.join(' '); + } + + return stringifiedArgs.join(' ' + this.op + ' '); + } else { + // fallback to formatting as a function call + return this.fn + '(' + this.args.join(', ') + ')'; + } + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + OperatorNode.prototype.toJSON = function () { + return { + mathjs: 'OperatorNode', + op: this.op, + fn: this.fn, + args: this.args, + implicit: this.implicit + }; + }; + /** + * Instantiate an OperatorNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "OperatorNode", "op": "+", "fn": "add", "args": [...], "implicit": false}`, + * where mathjs is optional + * @returns {OperatorNode} + */ + + + OperatorNode.fromJSON = function (json) { + return new OperatorNode(json.op, json.fn, json.args, json.implicit); + }; + /** + * Get HTML representation. + * @param {Object} options + * @return {string} str + */ + + + OperatorNode.prototype.toHTML = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var implicit = options && options.implicit ? options.implicit : 'hide'; + var args = this.args; + var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, false); + + if (args.length === 1) { + // unary operators + var assoc = operators.getAssociativity(this, parenthesis); + var operand = args[0].toHTML(options); + + if (parens[0]) { + operand = '<span class="math-parenthesis math-round-parenthesis">(</span>' + operand + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + if (assoc === 'right') { + // prefix operator + return '<span class="math-operator math-unary-operator math-lefthand-unary-operator">' + escape(this.op) + '</span>' + operand; + } else if (assoc === 'left') { + // postfix + return '<span class="math-operator math-unary-operator math-righthand-unary-operator">' + escape(this.op) + '</span>' + operand; + } // fall back to postfix + + + return '<span class="math-operator math-unary-operator math-righthand-unary-operator">' + escape(this.op) + '</span>' + operand; + } else if (args.length === 2) { + // binary operatoes + var lhs = args[0].toHTML(options); // left hand side + + var rhs = args[1].toHTML(options); // right hand side + + if (parens[0]) { + // left hand side in parenthesis? + lhs = '<span class="math-parenthesis math-round-parenthesis">(</span>' + lhs + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + if (parens[1]) { + // right hand side in parenthesis? + rhs = '<span class="math-parenthesis math-round-parenthesis">(</span>' + rhs + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') { + return lhs + '<span class="math-operator math-binary-operator math-implicit-binary-operator"></span>' + rhs; + } + + return lhs + '<span class="math-operator math-binary-operator math-explicit-binary-operator">' + escape(this.op) + '</span>' + rhs; + } else { + var stringifiedArgs = args.map(function (arg, index) { + arg = arg.toHTML(options); + + if (parens[index]) { + // put in parenthesis? + arg = '<span class="math-parenthesis math-round-parenthesis">(</span>' + arg + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + return arg; + }); + + if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) { + if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') { + return stringifiedArgs.join('<span class="math-operator math-binary-operator math-implicit-binary-operator"></span>'); + } + + return stringifiedArgs.join('<span class="math-operator math-binary-operator math-explicit-binary-operator">' + escape(this.op) + '</span>'); + } else { + // fallback to formatting as a function call + return '<span class="math-function">' + escape(this.fn) + '</span><span class="math-paranthesis math-round-parenthesis">(</span>' + stringifiedArgs.join('<span class="math-separator">,</span>') + '<span class="math-paranthesis math-round-parenthesis">)</span>'; + } + } + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + OperatorNode.prototype._toTex = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var implicit = options && options.implicit ? options.implicit : 'hide'; + var args = this.args; + var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, true); + var op = latex.operators[this.fn]; + op = typeof op === 'undefined' ? this.op : op; // fall back to using this.op + + if (args.length === 1) { + // unary operators + var assoc = operators.getAssociativity(this, parenthesis); + var operand = args[0].toTex(options); + + if (parens[0]) { + operand = "\\left(".concat(operand, "\\right)"); + } + + if (assoc === 'right') { + // prefix operator + return op + operand; + } else if (assoc === 'left') { + // postfix operator + return operand + op; + } // fall back to postfix + + + return operand + op; + } else if (args.length === 2) { + // binary operators + var lhs = args[0]; // left hand side + + var lhsTex = lhs.toTex(options); + + if (parens[0]) { + lhsTex = "\\left(".concat(lhsTex, "\\right)"); + } + + var rhs = args[1]; // right hand side + + var rhsTex = rhs.toTex(options); + + if (parens[1]) { + rhsTex = "\\left(".concat(rhsTex, "\\right)"); + } // handle some exceptions (due to the way LaTeX works) + + + var lhsIdentifier; + + if (parenthesis === 'keep') { + lhsIdentifier = lhs.getIdentifier(); + } else { + // Ignore ParenthesisNodes if in 'keep' mode + lhsIdentifier = lhs.getContent().getIdentifier(); + } + + switch (this.getIdentifier()) { + case 'OperatorNode:divide': + // op contains '\\frac' at this point + return op + '{' + lhsTex + '}' + '{' + rhsTex + '}'; + + case 'OperatorNode:pow': + lhsTex = '{' + lhsTex + '}'; + rhsTex = '{' + rhsTex + '}'; + + switch (lhsIdentifier) { + case 'ConditionalNode': // + + case 'OperatorNode:divide': + lhsTex = "\\left(".concat(lhsTex, "\\right)"); + } + + break; + + case 'OperatorNode:multiply': + if (this.implicit && implicit === 'hide') { + return lhsTex + '~' + rhsTex; + } + + } + + return lhsTex + op + rhsTex; + } else if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) { + var texifiedArgs = args.map(function (arg, index) { + arg = arg.toTex(options); + + if (parens[index]) { + arg = "\\left(".concat(arg, "\\right)"); + } + + return arg; + }); + + if (this.getIdentifier() === 'OperatorNode:multiply' && this.implicit) { + return texifiedArgs.join('~'); + } + + return texifiedArgs.join(op); + } else { + // fall back to formatting as a function call + // as this is a fallback, it doesn't use + // fancy function names + return '\\mathrm{' + this.fn + '}\\left(' + args.map(function (arg) { + return arg.toTex(options); + }).join(',') + '\\right)'; + } + }; + /** + * Get identifier. + * @return {string} + */ + + + OperatorNode.prototype.getIdentifier = function () { + return this.type + ':' + this.fn; + }; + + return OperatorNode; +} + +exports.name = 'OperatorNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../utils/array":46,"../../utils/customs":51,"../../utils/latex":53,"../../utils/string":57,"../operators":33,"./Node":24}],27:[function(require,module,exports){ +'use strict'; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + /** + * @constructor ParenthesisNode + * @extends {Node} + * A parenthesis node describes manual parenthesis from the user input + * @param {Node} content + * @extends {Node} + */ + + function ParenthesisNode(content) { + if (!(this instanceof ParenthesisNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } // validate input + + + if (!type.isNode(content)) { + throw new TypeError('Node expected for parameter "content"'); + } + + this.content = content; + } + + ParenthesisNode.prototype = new Node(); + ParenthesisNode.prototype.type = 'ParenthesisNode'; + ParenthesisNode.prototype.isParenthesisNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + ParenthesisNode.prototype._compile = function (math, argNames) { + return this.content._compile(math, argNames); + }; + /** + * Get the content of the current Node. + * @return {Node} content + * @override + **/ + + + ParenthesisNode.prototype.getContent = function () { + return this.content.getContent(); + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + ParenthesisNode.prototype.forEach = function (callback) { + callback(this.content, 'content', this); + }; + /** + * Create a new ParenthesisNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node) : Node} callback + * @returns {ParenthesisNode} Returns a clone of the node + */ + + + ParenthesisNode.prototype.map = function (callback) { + var content = callback(this.content, 'content', this); + return new ParenthesisNode(content); + }; + /** + * Create a clone of this node, a shallow copy + * @return {ParenthesisNode} + */ + + + ParenthesisNode.prototype.clone = function () { + return new ParenthesisNode(this.content); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + * @override + */ + + + ParenthesisNode.prototype._toString = function (options) { + if (!options || options && !options.parenthesis || options && options.parenthesis === 'keep') { + return '(' + this.content.toString(options) + ')'; + } + + return this.content.toString(options); + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + ParenthesisNode.prototype.toJSON = function () { + return { + mathjs: 'ParenthesisNode', + content: this.content + }; + }; + /** + * Instantiate an ParenthesisNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "ParenthesisNode", "content": ...}`, + * where mathjs is optional + * @returns {ParenthesisNode} + */ + + + ParenthesisNode.fromJSON = function (json) { + return new ParenthesisNode(json.content); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + * @override + */ + + + ParenthesisNode.prototype.toHTML = function (options) { + if (!options || options && !options.parenthesis || options && options.parenthesis === 'keep') { + return '<span class="math-parenthesis math-round-parenthesis">(</span>' + this.content.toHTML(options) + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + return this.content.toHTML(options); + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + * @override + */ + + + ParenthesisNode.prototype._toTex = function (options) { + if (!options || options && !options.parenthesis || options && options.parenthesis === 'keep') { + return "\\left(".concat(this.content.toTex(options), "\\right)"); + } + + return this.content.toTex(options); + }; + + return ParenthesisNode; +} + +exports.name = 'ParenthesisNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"./Node":24}],28:[function(require,module,exports){ +'use strict'; + +var operators = require('../operators'); + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + /** + * @constructor RangeNode + * @extends {Node} + * create a range + * @param {Node} start included lower-bound + * @param {Node} end included upper-bound + * @param {Node} [step] optional step + */ + + function RangeNode(start, end, step) { + if (!(this instanceof RangeNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } // validate inputs + + + if (!type.isNode(start)) throw new TypeError('Node expected'); + if (!type.isNode(end)) throw new TypeError('Node expected'); + if (step && !type.isNode(step)) throw new TypeError('Node expected'); + if (arguments.length > 3) throw new Error('Too many arguments'); + this.start = start; // included lower-bound + + this.end = end; // included upper-bound + + this.step = step || null; // optional step + } + + RangeNode.prototype = new Node(); + RangeNode.prototype.type = 'RangeNode'; + RangeNode.prototype.isRangeNode = true; + /** + * Check whether the RangeNode needs the `end` symbol to be defined. + * This end is the size of the Matrix in current dimension. + * @return {boolean} + */ + + RangeNode.prototype.needsEnd = function () { + // find all `end` symbols in this RangeNode + var endSymbols = this.filter(function (node) { + return type.isSymbolNode(node) && node.name === 'end'; + }); + return endSymbols.length > 0; + }; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + + RangeNode.prototype._compile = function (math, argNames) { + var range = math.range; + + var evalStart = this.start._compile(math, argNames); + + var evalEnd = this.end._compile(math, argNames); + + if (this.step) { + var evalStep = this.step._compile(math, argNames); + + return function evalRangeNode(scope, args, context) { + return range(evalStart(scope, args, context), evalEnd(scope, args, context), evalStep(scope, args, context)); + }; + } else { + return function evalRangeNode(scope, args, context) { + return range(evalStart(scope, args, context), evalEnd(scope, args, context)); + }; + } + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + RangeNode.prototype.forEach = function (callback) { + callback(this.start, 'start', this); + callback(this.end, 'end', this); + + if (this.step) { + callback(this.step, 'step', this); + } + }; + /** + * Create a new RangeNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {RangeNode} Returns a transformed copy of the node + */ + + + RangeNode.prototype.map = function (callback) { + return new RangeNode(this._ifNode(callback(this.start, 'start', this)), this._ifNode(callback(this.end, 'end', this)), this.step && this._ifNode(callback(this.step, 'step', this))); + }; + /** + * Create a clone of this node, a shallow copy + * @return {RangeNode} + */ + + + RangeNode.prototype.clone = function () { + return new RangeNode(this.start, this.end, this.step && this.step); + }; + /** + * Calculate the necessary parentheses + * @param {Node} node + * @param {string} parenthesis + * @return {Object} parentheses + * @private + */ + + + function calculateNecessaryParentheses(node, parenthesis) { + var precedence = operators.getPrecedence(node, parenthesis); + var parens = {}; + var startPrecedence = operators.getPrecedence(node.start, parenthesis); + parens.start = startPrecedence !== null && startPrecedence <= precedence || parenthesis === 'all'; + + if (node.step) { + var stepPrecedence = operators.getPrecedence(node.step, parenthesis); + parens.step = stepPrecedence !== null && stepPrecedence <= precedence || parenthesis === 'all'; + } + + var endPrecedence = operators.getPrecedence(node.end, parenthesis); + parens.end = endPrecedence !== null && endPrecedence <= precedence || parenthesis === 'all'; + return parens; + } + /** + * Get string representation + * @param {Object} options + * @return {string} str + */ + + + RangeNode.prototype._toString = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var parens = calculateNecessaryParentheses(this, parenthesis); // format string as start:step:stop + + var str; + var start = this.start.toString(options); + + if (parens.start) { + start = '(' + start + ')'; + } + + str = start; + + if (this.step) { + var step = this.step.toString(options); + + if (parens.step) { + step = '(' + step + ')'; + } + + str += ':' + step; + } + + var end = this.end.toString(options); + + if (parens.end) { + end = '(' + end + ')'; + } + + str += ':' + end; + return str; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + RangeNode.prototype.toJSON = function () { + return { + mathjs: 'RangeNode', + start: this.start, + end: this.end, + step: this.step + }; + }; + /** + * Instantiate an RangeNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "RangeNode", "start": ..., "end": ..., "step": ...}`, + * where mathjs is optional + * @returns {RangeNode} + */ + + + RangeNode.fromJSON = function (json) { + return new RangeNode(json.start, json.end, json.step); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + */ + + + RangeNode.prototype.toHTML = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var parens = calculateNecessaryParentheses(this, parenthesis); // format string as start:step:stop + + var str; + var start = this.start.toHTML(options); + + if (parens.start) { + start = '<span class="math-parenthesis math-round-parenthesis">(</span>' + start + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + str = start; + + if (this.step) { + var step = this.step.toHTML(options); + + if (parens.step) { + step = '<span class="math-parenthesis math-round-parenthesis">(</span>' + step + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + str += '<span class="math-operator math-range-operator">:</span>' + step; + } + + var end = this.end.toHTML(options); + + if (parens.end) { + end = '<span class="math-parenthesis math-round-parenthesis">(</span>' + end + '<span class="math-parenthesis math-round-parenthesis">)</span>'; + } + + str += '<span class="math-operator math-range-operator">:</span>' + end; + return str; + }; + /** + * Get LaTeX representation + * @params {Object} options + * @return {string} str + */ + + + RangeNode.prototype._toTex = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var parens = calculateNecessaryParentheses(this, parenthesis); + var str = this.start.toTex(options); + + if (parens.start) { + str = "\\left(".concat(str, "\\right)"); + } + + if (this.step) { + var step = this.step.toTex(options); + + if (parens.step) { + step = "\\left(".concat(step, "\\right)"); + } + + str += ':' + step; + } + + var end = this.end.toTex(options); + + if (parens.end) { + end = "\\left(".concat(end, "\\right)"); + } + + str += ':' + end; + return str; + }; + + return RangeNode; +} + +exports.name = 'RangeNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../operators":33,"./Node":24}],29:[function(require,module,exports){ +'use strict'; + +var operators = require('../operators'); + +var latex = require('../../utils/latex'); + +var escape = require('../../utils/string').escape; + +function factory(type, config, load, typed) { + var Node = load(require('./Node')); + + var getSafeProperty = require('../../utils/customs').getSafeProperty; + /** + * A node representing a chained conditional expression, such as 'x > y > z' + * + * @param {String[]} conditionals An array of conditional operators used to compare the parameters + * @param {Node[]} params The parameters that will be compared + * + * @constructor RelationalNode + * @extends {Node} + */ + + + function RelationalNode(conditionals, params) { + if (!(this instanceof RelationalNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + if (!Array.isArray(conditionals)) throw new TypeError('Parameter conditionals must be an array'); + if (!Array.isArray(params)) throw new TypeError('Parameter params must be an array'); + if (conditionals.length !== params.length - 1) throw new TypeError('Parameter params must contain exactly one more element than parameter conditionals'); + this.conditionals = conditionals; + this.params = params; + } + + RelationalNode.prototype = new Node(); + RelationalNode.prototype.type = 'RelationalNode'; + RelationalNode.prototype.isRelationalNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + RelationalNode.prototype._compile = function (math, argNames) { + var self = this; + var compiled = this.params.map(function (p) { + return p._compile(math, argNames); + }); + return function evalRelationalNode(scope, args, context) { + var evalLhs; + var evalRhs = compiled[0](scope, args, context); + + for (var i = 0; i < self.conditionals.length; i++) { + evalLhs = evalRhs; + evalRhs = compiled[i + 1](scope, args, context); + var condFn = getSafeProperty(math, self.conditionals[i]); + + if (!condFn(evalLhs, evalRhs)) { + return false; + } + } + + return true; + }; + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + RelationalNode.prototype.forEach = function (callback) { + var _this = this; + + this.params.forEach(function (n, i) { + return callback(n, 'params[' + i + ']', _this); + }, this); + }; + /** + * Create a new RelationalNode having its childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node): Node} callback + * @returns {RelationalNode} Returns a transformed copy of the node + */ + + + RelationalNode.prototype.map = function (callback) { + var _this2 = this; + + return new RelationalNode(this.conditionals.slice(), this.params.map(function (n, i) { + return _this2._ifNode(callback(n, 'params[' + i + ']', _this2)); + }, this)); + }; + /** + * Create a clone of this node, a shallow copy + * @return {RelationalNode} + */ + + + RelationalNode.prototype.clone = function () { + return new RelationalNode(this.conditionals, this.params); + }; + /** + * Get string representation. + * @param {Object} options + * @return {string} str + */ + + + RelationalNode.prototype._toString = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var precedence = operators.getPrecedence(this, parenthesis); + var paramStrings = this.params.map(function (p, index) { + var paramPrecedence = operators.getPrecedence(p, parenthesis); + return parenthesis === 'all' || paramPrecedence !== null && paramPrecedence <= precedence ? '(' + p.toString(options) + ')' : p.toString(options); + }); + var operatorMap = { + 'equal': '==', + 'unequal': '!=', + 'smaller': '<', + 'larger': '>', + 'smallerEq': '<=', + 'largerEq': '>=' + }; + var ret = paramStrings[0]; + + for (var i = 0; i < this.conditionals.length; i++) { + ret += ' ' + operatorMap[this.conditionals[i]] + ' ' + paramStrings[i + 1]; + } + + return ret; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + RelationalNode.prototype.toJSON = function () { + return { + mathjs: 'RelationalNode', + conditionals: this.conditionals, + params: this.params + }; + }; + /** + * Instantiate a RelationalNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "RelationalNode", "condition": ..., "trueExpr": ..., "falseExpr": ...}`, + * where mathjs is optional + * @returns {RelationalNode} + */ + + + RelationalNode.fromJSON = function (json) { + return new RelationalNode(json.conditionals, json.params); + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + */ + + + RelationalNode.prototype.toHTML = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var precedence = operators.getPrecedence(this, parenthesis); + var paramStrings = this.params.map(function (p, index) { + var paramPrecedence = operators.getPrecedence(p, parenthesis); + return parenthesis === 'all' || paramPrecedence !== null && paramPrecedence <= precedence ? '<span class="math-parenthesis math-round-parenthesis">(</span>' + p.toHTML(options) + '<span class="math-parenthesis math-round-parenthesis">)</span>' : p.toHTML(options); + }); + var operatorMap = { + 'equal': '==', + 'unequal': '!=', + 'smaller': '<', + 'larger': '>', + 'smallerEq': '<=', + 'largerEq': '>=' + }; + var ret = paramStrings[0]; + + for (var i = 0; i < this.conditionals.length; i++) { + ret += '<span class="math-operator math-binary-operator math-explicit-binary-operator">' + escape(operatorMap[this.conditionals[i]]) + '</span>' + paramStrings[i + 1]; + } + + return ret; + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + */ + + + RelationalNode.prototype._toTex = function (options) { + var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; + var precedence = operators.getPrecedence(this, parenthesis); + var paramStrings = this.params.map(function (p, index) { + var paramPrecedence = operators.getPrecedence(p, parenthesis); + return parenthesis === 'all' || paramPrecedence !== null && paramPrecedence <= precedence ? '\\left(' + p.toString(options) + '\right)' : p.toString(options); + }); + var ret = paramStrings[0]; + + for (var i = 0; i < this.conditionals.length; i++) { + ret += latex.operators[this.conditionals[i]] + paramStrings[i + 1]; + } + + return ret; + }; + + return RelationalNode; +} + +exports.name = 'RelationalNode'; +exports.path = 'expression.node'; +exports.factory = factory; +},{"../../utils/customs":51,"../../utils/latex":53,"../../utils/string":57,"../operators":33,"./Node":24}],30:[function(require,module,exports){ +'use strict'; + +var latex = require('../../utils/latex'); + +var escape = require('../../utils/string').escape; + +var hasOwnProperty = require('../../utils/object').hasOwnProperty; + +var getSafeProperty = require('../../utils/customs').getSafeProperty; + +function factory(type, config, load, typed, math) { + var Node = load(require('./Node')); + /** + * Check whether some name is a valueless unit like "inch". + * @param {string} name + * @return {boolean} + */ + + function isValuelessUnit(name) { + return type.Unit ? type.Unit.isValuelessUnit(name) : false; + } + /** + * @constructor SymbolNode + * @extends {Node} + * A symbol node can hold and resolve a symbol + * @param {string} name + * @extends {Node} + */ + + + function SymbolNode(name) { + if (!(this instanceof SymbolNode)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } // validate input + + + if (typeof name !== 'string') throw new TypeError('String expected for parameter "name"'); + this.name = name; + } + + SymbolNode.prototype = new Node(); + SymbolNode.prototype.type = 'SymbolNode'; + SymbolNode.prototype.isSymbolNode = true; + /** + * Compile a node into a JavaScript function. + * This basically pre-calculates as much as possible and only leaves open + * calculations which depend on a dynamic scope with variables. + * @param {Object} math Math.js namespace with functions and constants. + * @param {Object} argNames An object with argument names as key and `true` + * as value. Used in the SymbolNode to optimize + * for arguments from user assigned functions + * (see FunctionAssignmentNode) or special symbols + * like `end` (see IndexNode). + * @return {function} Returns a function which can be called like: + * evalNode(scope: Object, args: Object, context: *) + */ + + SymbolNode.prototype._compile = function (math, argNames) { + var name = this.name; + + if (hasOwnProperty(argNames, name)) { + // this is a FunctionAssignment argument + // (like an x when inside the expression of a function assignment `f(x) = ...`) + return function (scope, args, context) { + return args[name]; + }; + } else if (name in math) { + return function (scope, args, context) { + return name in scope ? getSafeProperty(scope, name) : getSafeProperty(math, name); + }; + } else { + var isUnit = isValuelessUnit(name); + return function (scope, args, context) { + return name in scope ? getSafeProperty(scope, name) : isUnit ? new type.Unit(null, name) : undef(name); + }; + } + }; + /** + * Execute a callback for each of the child nodes of this node + * @param {function(child: Node, path: string, parent: Node)} callback + */ + + + SymbolNode.prototype.forEach = function (callback) {} // nothing to do, we don't have childs + + /** + * Create a new SymbolNode having it's childs be the results of calling + * the provided callback function for each of the childs of the original node. + * @param {function(child: Node, path: string, parent: Node) : Node} callback + * @returns {SymbolNode} Returns a clone of the node + */ + ; + + SymbolNode.prototype.map = function (callback) { + return this.clone(); + }; + /** + * Throws an error 'Undefined symbol {name}' + * @param {string} name + */ + + + function undef(name) { + throw new Error('Undefined symbol ' + name); + } + /** + * Create a clone of this node, a shallow copy + * @return {SymbolNode} + */ + + + SymbolNode.prototype.clone = function () { + return new SymbolNode(this.name); + }; + /** + * Get string representation + * @param {Object} options + * @return {string} str + * @override + */ + + + SymbolNode.prototype._toString = function (options) { + return this.name; + }; + /** + * Get HTML representation + * @param {Object} options + * @return {string} str + * @override + */ + + + SymbolNode.prototype.toHTML = function (options) { + var name = escape(this.name); + + if (name === 'true' || name === 'false') { + return '<span class="math-symbol math-boolean">' + name + '</span>'; + } else if (name === 'i') { + return '<span class="math-symbol math-imaginary-symbol">' + name + '</span>'; + } else if (name === 'Infinity') { + return '<span class="math-symbol math-infinity-symbol">' + name + '</span>'; + } else if (name === 'NaN') { + return '<span class="math-symbol math-nan-symbol">' + name + '</span>'; + } else if (name === 'null') { + return '<span class="math-symbol math-null-symbol">' + name + '</span>'; + } else if (name === 'undefined') { + return '<span class="math-symbol math-undefined-symbol">' + name + '</span>'; + } + + return '<span class="math-symbol">' + name + '</span>'; + }; + /** + * Get a JSON representation of the node + * @returns {Object} + */ + + + SymbolNode.prototype.toJSON = function () { + return { + mathjs: 'SymbolNode', + name: this.name + }; + }; + /** + * Instantiate a SymbolNode from its JSON representation + * @param {Object} json An object structured like + * `{"mathjs": "SymbolNode", name: "x"}`, + * where mathjs is optional + * @returns {SymbolNode} + */ + + + SymbolNode.fromJSON = function (json) { + return new SymbolNode(json.name); + }; + /** + * Get LaTeX representation + * @param {Object} options + * @return {string} str + * @override + */ + + + SymbolNode.prototype._toTex = function (options) { + var isUnit = false; + + if (typeof math[this.name] === 'undefined' && isValuelessUnit(this.name)) { + isUnit = true; + } + + var symbol = latex.toSymbol(this.name, isUnit); + + if (symbol[0] === '\\') { + // no space needed if the symbol starts with '\' + return symbol; + } // the space prevents symbols from breaking stuff like '\cdot' if it's written right before the symbol + + + return ' ' + symbol; + }; + + return SymbolNode; +} + +exports.name = 'SymbolNode'; +exports.path = 'expression.node'; +exports.math = true; // request access to the math namespace as 5th argument of the factory function + +exports.factory = factory; +},{"../../utils/customs":51,"../../utils/latex":53,"../../utils/object":55,"../../utils/string":57,"./Node":24}],31:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var errorTransform = require('../../transform/error.transform').transform; + +var getSafeProperty = require('../../../utils/customs').getSafeProperty; + +function factory(type, config, load, typed) { + var subset = load(require('../../../function/matrix/subset')); + /** + * Retrieve part of an object: + * + * - Retrieve a property from an object + * - Retrieve a part of a string + * - Retrieve a matrix subset + * + * @param {Object | Array | Matrix | string} object + * @param {Index} index + * @return {Object | Array | Matrix | string} Returns the subset + */ + + return function access(object, index) { + try { + if (Array.isArray(object)) { + return subset(object, index); + } else if (object && typeof object.subset === 'function') { + // Matrix + return object.subset(index); + } else if (typeof object === 'string') { + // TODO: move getStringSubset into a separate util file, use that + return subset(object, index); + } else if (_typeof(object) === 'object') { + if (!index.isObjectProperty()) { + throw new TypeError('Cannot apply a numeric index as object property'); + } + + return getSafeProperty(object, index.getObjectProperty()); + } else { + throw new TypeError('Cannot apply index: unsupported type of object'); + } + } catch (err) { + throw errorTransform(err); + } + }; +} + +exports.factory = factory; +},{"../../../function/matrix/subset":36,"../../../utils/customs":51,"../../transform/error.transform":35}],32:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var errorTransform = require('../../transform/error.transform').transform; + +var setSafeProperty = require('../../../utils/customs').setSafeProperty; + +function factory(type, config, load, typed) { + var subset = load(require('../../../function/matrix/subset')); + var matrix = load(require('../../../type/matrix/function/matrix')); + /** + * Replace part of an object: + * + * - Assign a property to an object + * - Replace a part of a string + * - Replace a matrix subset + * + * @param {Object | Array | Matrix | string} object + * @param {Index} index + * @param {*} value + * @return {Object | Array | Matrix | string} Returns the original object + * except in case of a string + */ + // TODO: change assign to return the value instead of the object + + return function assign(object, index, value) { + try { + if (Array.isArray(object)) { + return matrix(object).subset(index, value).valueOf(); + } else if (object && typeof object.subset === 'function') { + // Matrix + return object.subset(index, value); + } else if (typeof object === 'string') { + // TODO: move setStringSubset into a separate util file, use that + return subset(object, index, value); + } else if (_typeof(object) === 'object') { + if (!index.isObjectProperty()) { + throw TypeError('Cannot apply a numeric index as object property'); + } + + setSafeProperty(object, index.getObjectProperty(), value); + return object; + } else { + throw new TypeError('Cannot apply index: unsupported type of object'); + } + } catch (err) { + throw errorTransform(err); + } + }; +} + +exports.factory = factory; +},{"../../../function/matrix/subset":36,"../../../type/matrix/function/matrix":42,"../../../utils/customs":51,"../../transform/error.transform":35}],33:[function(require,module,exports){ +'use strict'; // list of identifiers of nodes in order of their precedence +// also contains information about left/right associativity +// and which other operator the operator is associative with +// Example: +// addition is associative with addition and subtraction, because: +// (a+b)+c=a+(b+c) +// (a+b)-c=a+(b-c) +// +// postfix operators are left associative, prefix operators +// are right associative +// +// It's also possible to set the following properties: +// latexParens: if set to false, this node doesn't need to be enclosed +// in parentheses when using LaTeX +// latexLeftParens: if set to false, this !OperatorNode's! +// left argument doesn't need to be enclosed +// in parentheses +// latexRightParens: the same for the right argument + +var properties = [{ + // assignment + 'AssignmentNode': {}, + 'FunctionAssignmentNode': {} +}, { + // conditional expression + 'ConditionalNode': { + latexLeftParens: false, + latexRightParens: false, + latexParens: false // conditionals don't need parentheses in LaTeX because + // they are 2 dimensional + + } +}, { + // logical or + 'OperatorNode:or': { + associativity: 'left', + associativeWith: [] + } +}, { + // logical xor + 'OperatorNode:xor': { + associativity: 'left', + associativeWith: [] + } +}, { + // logical and + 'OperatorNode:and': { + associativity: 'left', + associativeWith: [] + } +}, { + // bitwise or + 'OperatorNode:bitOr': { + associativity: 'left', + associativeWith: [] + } +}, { + // bitwise xor + 'OperatorNode:bitXor': { + associativity: 'left', + associativeWith: [] + } +}, { + // bitwise and + 'OperatorNode:bitAnd': { + associativity: 'left', + associativeWith: [] + } +}, { + // relational operators + 'OperatorNode:equal': { + associativity: 'left', + associativeWith: [] + }, + 'OperatorNode:unequal': { + associativity: 'left', + associativeWith: [] + }, + 'OperatorNode:smaller': { + associativity: 'left', + associativeWith: [] + }, + 'OperatorNode:larger': { + associativity: 'left', + associativeWith: [] + }, + 'OperatorNode:smallerEq': { + associativity: 'left', + associativeWith: [] + }, + 'OperatorNode:largerEq': { + associativity: 'left', + associativeWith: [] + }, + 'RelationalNode': { + associativity: 'left', + associativeWith: [] + } +}, { + // bitshift operators + 'OperatorNode:leftShift': { + associativity: 'left', + associativeWith: [] + }, + 'OperatorNode:rightArithShift': { + associativity: 'left', + associativeWith: [] + }, + 'OperatorNode:rightLogShift': { + associativity: 'left', + associativeWith: [] + } +}, { + // unit conversion + 'OperatorNode:to': { + associativity: 'left', + associativeWith: [] + } +}, { + // range + 'RangeNode': {} +}, { + // addition, subtraction + 'OperatorNode:add': { + associativity: 'left', + associativeWith: ['OperatorNode:add', 'OperatorNode:subtract'] + }, + 'OperatorNode:subtract': { + associativity: 'left', + associativeWith: [] + } +}, { + // multiply, divide, modulus + 'OperatorNode:multiply': { + associativity: 'left', + associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'Operator:dotMultiply', 'Operator:dotDivide'] + }, + 'OperatorNode:divide': { + associativity: 'left', + associativeWith: [], + latexLeftParens: false, + latexRightParens: false, + latexParens: false // fractions don't require parentheses because + // they're 2 dimensional, so parens aren't needed + // in LaTeX + + }, + 'OperatorNode:dotMultiply': { + associativity: 'left', + associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'OperatorNode:dotMultiply', 'OperatorNode:doDivide'] + }, + 'OperatorNode:dotDivide': { + associativity: 'left', + associativeWith: [] + }, + 'OperatorNode:mod': { + associativity: 'left', + associativeWith: [] + } +}, { + // unary prefix operators + 'OperatorNode:unaryPlus': { + associativity: 'right' + }, + 'OperatorNode:unaryMinus': { + associativity: 'right' + }, + 'OperatorNode:bitNot': { + associativity: 'right' + }, + 'OperatorNode:not': { + associativity: 'right' + } +}, { + // exponentiation + 'OperatorNode:pow': { + associativity: 'right', + associativeWith: [], + latexRightParens: false // the exponent doesn't need parentheses in + // LaTeX because it's 2 dimensional + // (it's on top) + + }, + 'OperatorNode:dotPow': { + associativity: 'right', + associativeWith: [] + } +}, { + // factorial + 'OperatorNode:factorial': { + associativity: 'left' + } +}, { + // matrix transpose + 'OperatorNode:transpose': { + associativity: 'left' + } +}]; +/** + * Get the precedence of a Node. + * Higher number for higher precedence, starting with 0. + * Returns null if the precedence is undefined. + * + * @param {Node} + * @param {string} parenthesis + * @return {number|null} + */ + +function getPrecedence(_node, parenthesis) { + var node = _node; + + if (parenthesis !== 'keep') { + // ParenthesisNodes are only ignored when not in 'keep' mode + node = _node.getContent(); + } + + var identifier = node.getIdentifier(); + + for (var i = 0; i < properties.length; i++) { + if (identifier in properties[i]) { + return i; + } + } + + return null; +} +/** + * Get the associativity of an operator (left or right). + * Returns a string containing 'left' or 'right' or null if + * the associativity is not defined. + * + * @param {Node} + * @param {string} parenthesis + * @return {string|null} + * @throws {Error} + */ + + +function getAssociativity(_node, parenthesis) { + var node = _node; + + if (parenthesis !== 'keep') { + // ParenthesisNodes are only ignored when not in 'keep' mode + node = _node.getContent(); + } + + var identifier = node.getIdentifier(); + var index = getPrecedence(node, parenthesis); + + if (index === null) { + // node isn't in the list + return null; + } + + var property = properties[index][identifier]; + + if (property.hasOwnProperty('associativity')) { + if (property.associativity === 'left') { + return 'left'; + } + + if (property.associativity === 'right') { + return 'right'; + } // associativity is invalid + + + throw Error('\'' + identifier + '\' has the invalid associativity \'' + property.associativity + '\'.'); + } // associativity is undefined + + + return null; +} +/** + * Check if an operator is associative with another operator. + * Returns either true or false or null if not defined. + * + * @param {Node} nodeA + * @param {Node} nodeB + * @param {string} parenthesis + * @return {bool|null} + */ + + +function isAssociativeWith(nodeA, nodeB, parenthesis) { + // ParenthesisNodes are only ignored when not in 'keep' mode + var a = parenthesis !== 'keep' ? nodeA.getContent() : nodeA; + var b = parenthesis !== 'keep' ? nodeA.getContent() : nodeB; + var identifierA = a.getIdentifier(); + var identifierB = b.getIdentifier(); + var index = getPrecedence(a, parenthesis); + + if (index === null) { + // node isn't in the list + return null; + } + + var property = properties[index][identifierA]; + + if (property.hasOwnProperty('associativeWith') && property.associativeWith instanceof Array) { + for (var i = 0; i < property.associativeWith.length; i++) { + if (property.associativeWith[i] === identifierB) { + return true; + } + } + + return false; + } // associativeWith is not defined + + + return null; +} + +module.exports.properties = properties; +module.exports.getPrecedence = getPrecedence; +module.exports.getAssociativity = getAssociativity; +module.exports.isAssociativeWith = isAssociativeWith; +},{}],34:[function(require,module,exports){ +'use strict'; + +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +var ArgumentsError = require('../error/ArgumentsError'); + +var deepMap = require('../utils/collection/deepMap'); + +function factory(type, config, load, typed) { + var numeric = load(require('../type/numeric')); + var AccessorNode = load(require('./node/AccessorNode')); + var ArrayNode = load(require('./node/ArrayNode')); + var AssignmentNode = load(require('./node/AssignmentNode')); + var BlockNode = load(require('./node/BlockNode')); + var ConditionalNode = load(require('./node/ConditionalNode')); + var ConstantNode = load(require('./node/ConstantNode')); + var FunctionAssignmentNode = load(require('./node/FunctionAssignmentNode')); + var IndexNode = load(require('./node/IndexNode')); + var ObjectNode = load(require('./node/ObjectNode')); + var OperatorNode = load(require('./node/OperatorNode')); + var ParenthesisNode = load(require('./node/ParenthesisNode')); + var FunctionNode = load(require('./node/FunctionNode')); + var RangeNode = load(require('./node/RangeNode')); + var RelationalNode = load(require('./node/RelationalNode')); + var SymbolNode = load(require('./node/SymbolNode')); + /** + * Parse an expression. Returns a node tree, which can be evaluated by + * invoking node.eval(). + * + * Syntax: + * + * parse(expr) + * parse(expr, options) + * parse([expr1, expr2, expr3, ...]) + * parse([expr1, expr2, expr3, ...], options) + * + * Example: + * + * const node = parse('sqrt(3^2 + 4^2)') + * node.compile(math).eval() // 5 + * + * let scope = {a:3, b:4} + * const node = parse('a * b') // 12 + * const code = node.compile(math) + * code.eval(scope) // 12 + * scope.a = 5 + * code.eval(scope) // 20 + * + * const nodes = math.parse(['a = 3', 'b = 4', 'a * b']) + * nodes[2].compile(math).eval() // 12 + * + * @param {string | string[] | Matrix} expr + * @param {{nodes: Object<string, Node>}} [options] Available options: + * - `nodes` a set of custom nodes + * @return {Node | Node[]} node + * @throws {Error} + */ + + function parse(expr, options) { + if (arguments.length !== 1 && arguments.length !== 2) { + throw new ArgumentsError('parse', arguments.length, 1, 2); + } // pass extra nodes + + + var extraNodes = options && options.nodes ? options.nodes : {}; + + if (typeof expr === 'string') { + // parse a single expression + return parseStart(expr, extraNodes); + } else if (Array.isArray(expr) || expr instanceof type.Matrix) { + // parse an array or matrix with expressions + return deepMap(expr, function (elem) { + if (typeof elem !== 'string') throw new TypeError('String expected'); + return parseStart(elem, extraNodes); + }); + } else { + // oops + throw new TypeError('String or matrix expected'); + } + } // token types enumeration + + + var TOKENTYPE = { + NULL: 0, + DELIMITER: 1, + NUMBER: 2, + SYMBOL: 3, + UNKNOWN: 4 // map with all delimiters + + }; + var DELIMITERS = { + ',': true, + '(': true, + ')': true, + '[': true, + ']': true, + '{': true, + '}': true, + '"': true, + '\'': true, + ';': true, + '+': true, + '-': true, + '*': true, + '.*': true, + '/': true, + './': true, + '%': true, + '^': true, + '.^': true, + '~': true, + '!': true, + '&': true, + '|': true, + '^|': true, + '=': true, + ':': true, + '?': true, + '==': true, + '!=': true, + '<': true, + '>': true, + '<=': true, + '>=': true, + '<<': true, + '>>': true, + '>>>': true // map with all named delimiters + + }; + var NAMED_DELIMITERS = { + 'mod': true, + 'to': true, + 'in': true, + 'and': true, + 'xor': true, + 'or': true, + 'not': true + }; + var CONSTANTS = { + 'true': true, + 'false': false, + 'null': null, + 'undefined': undefined + }; + var NUMERIC_CONSTANTS = ['NaN', 'Infinity']; + + function initialState() { + return { + extraNodes: {}, + // current extra nodes, must be careful not to mutate + expression: '', + // current expression + comment: '', + // last parsed comment + index: 0, + // current index in expr + token: '', + // current token + tokenType: TOKENTYPE.NULL, + // type of the token + nestingLevel: 0, + // level of nesting inside parameters, used to ignore newline characters + conditionalLevel: null // when a conditional is being parsed, the level of the conditional is stored here + + }; + } + /** + * View upto `length` characters of the expression starting at the current character. + * + * @param {State} state + * @param {number} [length=1] Number of characters to view + * @returns {string} + * @private + */ + + + function currentString(state, length) { + return state.expression.substr(state.index, length); + } + /** + * View the current character. Returns '' if end of expression is reached. + * + * @param {State} state + * @returns {string} + * @private + */ + + + function currentCharacter(state) { + return currentString(state, 1); + } + /** + * Get the next character from the expression. + * The character is stored into the char c. If the end of the expression is + * reached, the function puts an empty string in c. + * @private + */ + + + function next(state) { + state.index++; + } + /** + * Preview the previous character from the expression. + * @return {string} cNext + * @private + */ + + + function prevCharacter(state) { + return state.expression.charAt(state.index - 1); + } + /** + * Preview the next character from the expression. + * @return {string} cNext + * @private + */ + + + function nextCharacter(state) { + return state.expression.charAt(state.index + 1); + } + /** + * Get next token in the current string expr. + * The token and token type are available as token and tokenType + * @private + */ + + + function getToken(state) { + state.tokenType = TOKENTYPE.NULL; + state.token = ''; + state.comment = ''; // skip over whitespaces + // space, tab, and newline when inside parameters + + while (parse.isWhitespace(currentCharacter(state), state.nestingLevel)) { + next(state); + } // skip comment + + + if (currentCharacter(state) === '#') { + while (currentCharacter(state) !== '\n' && currentCharacter(state) !== '') { + state.comment += currentCharacter(state); + next(state); + } + } // check for end of expression + + + if (currentCharacter(state) === '') { + // token is still empty + state.tokenType = TOKENTYPE.DELIMITER; + return; + } // check for new line character + + + if (currentCharacter(state) === '\n' && !state.nestingLevel) { + state.tokenType = TOKENTYPE.DELIMITER; + state.token = currentCharacter(state); + next(state); + return; + } + + var c1 = currentCharacter(state); + var c2 = currentString(state, 2); + var c3 = currentString(state, 3); + + if (c3.length === 3 && DELIMITERS[c3]) { + state.tokenType = TOKENTYPE.DELIMITER; + state.token = c3; + next(state); + next(state); + next(state); + return; + } // check for delimiters consisting of 2 characters + + + if (c2.length === 2 && DELIMITERS[c2]) { + state.tokenType = TOKENTYPE.DELIMITER; + state.token = c2; + next(state); + next(state); + return; + } // check for delimiters consisting of 1 character + + + if (DELIMITERS[c1]) { + state.tokenType = TOKENTYPE.DELIMITER; + state.token = c1; + next(state); + return; + } // check for a number + + + if (parse.isDigitDot(c1)) { + state.tokenType = TOKENTYPE.NUMBER; // get number, can have a single dot + + if (currentCharacter(state) === '.') { + state.token += currentCharacter(state); + next(state); + + if (!parse.isDigit(currentCharacter(state))) { + // this is no number, it is just a dot (can be dot notation) + state.tokenType = TOKENTYPE.DELIMITER; + } + } else { + while (parse.isDigit(currentCharacter(state))) { + state.token += currentCharacter(state); + next(state); + } + + if (parse.isDecimalMark(currentCharacter(state), nextCharacter(state))) { + state.token += currentCharacter(state); + next(state); + } + } + + while (parse.isDigit(currentCharacter(state))) { + state.token += currentCharacter(state); + next(state); + } // check for exponential notation like "2.3e-4", "1.23e50" or "2e+4" + + + if (currentCharacter(state) === 'E' || currentCharacter(state) === 'e') { + if (parse.isDigit(nextCharacter(state)) || nextCharacter(state) === '-' || nextCharacter(state) === '+') { + state.token += currentCharacter(state); + next(state); + + if (currentCharacter(state) === '+' || currentCharacter(state) === '-') { + state.token += currentCharacter(state); + next(state); + } // Scientific notation MUST be followed by an exponent + + + if (!parse.isDigit(currentCharacter(state))) { + throw createSyntaxError(state, 'Digit expected, got "' + currentCharacter(state) + '"'); + } + + while (parse.isDigit(currentCharacter(state))) { + state.token += currentCharacter(state); + next(state); + } + + if (parse.isDecimalMark(currentCharacter(state), nextCharacter(state))) { + throw createSyntaxError(state, 'Digit expected, got "' + currentCharacter(state) + '"'); + } + } else if (nextCharacter(state) === '.') { + next(state); + throw createSyntaxError(state, 'Digit expected, got "' + currentCharacter(state) + '"'); + } + } + + return; + } // check for variables, functions, named operators + + + if (parse.isAlpha(currentCharacter(state), prevCharacter(state), nextCharacter(state))) { + while (parse.isAlpha(currentCharacter(state), prevCharacter(state), nextCharacter(state)) || parse.isDigit(currentCharacter(state))) { + state.token += currentCharacter(state); + next(state); + } + + if (NAMED_DELIMITERS.hasOwnProperty(state.token)) { + state.tokenType = TOKENTYPE.DELIMITER; + } else { + state.tokenType = TOKENTYPE.SYMBOL; + } + + return; + } // something unknown is found, wrong characters -> a syntax error + + + state.tokenType = TOKENTYPE.UNKNOWN; + + while (currentCharacter(state) !== '') { + state.token += currentCharacter(state); + next(state); + } + + throw createSyntaxError(state, 'Syntax error in part "' + state.token + '"'); + } + /** + * Get next token and skip newline tokens + */ + + + function getTokenSkipNewline(state) { + do { + getToken(state); + } while (state.token === '\n'); // eslint-disable-line no-unmodified-loop-condition + + } + /** + * Open parameters. + * New line characters will be ignored until closeParams(state) is called + */ + + + function openParams(state) { + state.nestingLevel++; + } + /** + * Close parameters. + * New line characters will no longer be ignored + */ + + + function closeParams(state) { + state.nestingLevel--; + } + /** + * Checks whether the current character `c` is a valid alpha character: + * + * - A latin letter (upper or lower case) Ascii: a-z, A-Z + * - An underscore Ascii: _ + * - A dollar sign Ascii: $ + * - A latin letter with accents Unicode: \u00C0 - \u02AF + * - A greek letter Unicode: \u0370 - \u03FF + * - A mathematical alphanumeric symbol Unicode: \u{1D400} - \u{1D7FF} excluding invalid code points + * + * The previous and next characters are needed to determine whether + * this character is part of a unicode surrogate pair. + * + * @param {string} c Current character in the expression + * @param {string} cPrev Previous character + * @param {string} cNext Next character + * @return {boolean} + */ + + + parse.isAlpha = function isAlpha(c, cPrev, cNext) { + return parse.isValidLatinOrGreek(c) || parse.isValidMathSymbol(c, cNext) || parse.isValidMathSymbol(cPrev, c); + }; + /** + * Test whether a character is a valid latin, greek, or letter-like character + * @param {string} c + * @return {boolean} + */ + + + parse.isValidLatinOrGreek = function isValidLatinOrGreek(c) { + return /^[a-zA-Z_$\u00C0-\u02AF\u0370-\u03FF\u2100-\u214F]$/.test(c); + }; + /** + * Test whether two given 16 bit characters form a surrogate pair of a + * unicode math symbol. + * + * http://unicode-table.com/en/ + * http://www.wikiwand.com/en/Mathematical_operators_and_symbols_in_Unicode + * + * Note: In ES6 will be unicode aware: + * http://stackoverflow.com/questions/280712/javascript-unicode-regexes + * https://mathiasbynens.be/notes/es6-unicode-regex + * + * @param {string} high + * @param {string} low + * @return {boolean} + */ + + + parse.isValidMathSymbol = function isValidMathSymbol(high, low) { + return /^[\uD835]$/.test(high) && /^[\uDC00-\uDFFF]$/.test(low) && /^[^\uDC55\uDC9D\uDCA0\uDCA1\uDCA3\uDCA4\uDCA7\uDCA8\uDCAD\uDCBA\uDCBC\uDCC4\uDD06\uDD0B\uDD0C\uDD15\uDD1D\uDD3A\uDD3F\uDD45\uDD47-\uDD49\uDD51\uDEA6\uDEA7\uDFCC\uDFCD]$/.test(low); + }; + /** + * Check whether given character c is a white space character: space, tab, or enter + * @param {string} c + * @param {number} nestingLevel + * @return {boolean} + */ + + + parse.isWhitespace = function isWhitespace(c, nestingLevel) { + // TODO: also take '\r' carriage return as newline? Or does that give problems on mac? + return c === ' ' || c === '\t' || c === '\n' && nestingLevel > 0; + }; + /** + * Test whether the character c is a decimal mark (dot). + * This is the case when it's not the start of a delimiter '.*', './', or '.^' + * @param {string} c + * @param {string} cNext + * @return {boolean} + */ + + + parse.isDecimalMark = function isDecimalMark(c, cNext) { + return c === '.' && cNext !== '/' && cNext !== '*' && cNext !== '^'; + }; + /** + * checks if the given char c is a digit or dot + * @param {string} c a string with one character + * @return {boolean} + */ + + + parse.isDigitDot = function isDigitDot(c) { + return c >= '0' && c <= '9' || c === '.'; + }; + /** + * checks if the given char c is a digit + * @param {string} c a string with one character + * @return {boolean} + */ + + + parse.isDigit = function isDigit(c) { + return c >= '0' && c <= '9'; + }; + /** + * Start of the parse levels below, in order of precedence + * @return {Node} node + * @private + */ + + + function parseStart(expression, extraNodes) { + var state = initialState(); + + _extends(state, { + expression: expression, + extraNodes: extraNodes + }); + + getToken(state); + var node = parseBlock(state); // check for garbage at the end of the expression + // an expression ends with a empty character '' and tokenType DELIMITER + + if (state.token !== '') { + if (state.tokenType === TOKENTYPE.DELIMITER) { + // user entered a not existing operator like "//" + // TODO: give hints for aliases, for example with "<>" give as hint " did you mean !== ?" + throw createError(state, 'Unexpected operator ' + state.token); + } else { + throw createSyntaxError(state, 'Unexpected part "' + state.token + '"'); + } + } + + return node; + } + /** + * Parse a block with expressions. Expressions can be separated by a newline + * character '\n', or by a semicolon ';'. In case of a semicolon, no output + * of the preceding line is returned. + * @return {Node} node + * @private + */ + + + function parseBlock(state) { + var node; + var blocks = []; + var visible; + + if (state.token !== '' && state.token !== '\n' && state.token !== ';') { + node = parseAssignment(state); + node.comment = state.comment; + } // TODO: simplify this loop + + + while (state.token === '\n' || state.token === ';') { + // eslint-disable-line no-unmodified-loop-condition + if (blocks.length === 0 && node) { + visible = state.token !== ';'; + blocks.push({ + node: node, + visible: visible + }); + } + + getToken(state); + + if (state.token !== '\n' && state.token !== ';' && state.token !== '') { + node = parseAssignment(state); + node.comment = state.comment; + visible = state.token !== ';'; + blocks.push({ + node: node, + visible: visible + }); + } + } + + if (blocks.length > 0) { + return new BlockNode(blocks); + } else { + if (!node) { + node = new ConstantNode(undefined); + node.comment = state.comment; + } + + return node; + } + } + /** + * Assignment of a function or variable, + * - can be a variable like 'a=2.3' + * - or a updating an existing variable like 'matrix(2,3:5)=[6,7,8]' + * - defining a function like 'f(x) = x^2' + * @return {Node} node + * @private + */ + + + function parseAssignment(state) { + var name, args, value, valid; + var node = parseConditional(state); + + if (state.token === '=') { + if (type.isSymbolNode(node)) { + // parse a variable assignment like 'a = 2/3' + name = node.name; + getTokenSkipNewline(state); + value = parseAssignment(state); + return new AssignmentNode(new SymbolNode(name), value); + } else if (type.isAccessorNode(node)) { + // parse a matrix subset assignment like 'A[1,2] = 4' + getTokenSkipNewline(state); + value = parseAssignment(state); + return new AssignmentNode(node.object, node.index, value); + } else if (type.isFunctionNode(node) && type.isSymbolNode(node.fn)) { + // parse function assignment like 'f(x) = x^2' + valid = true; + args = []; + name = node.name; + node.args.forEach(function (arg, index) { + if (type.isSymbolNode(arg)) { + args[index] = arg.name; + } else { + valid = false; + } + }); + + if (valid) { + getTokenSkipNewline(state); + value = parseAssignment(state); + return new FunctionAssignmentNode(name, args, value); + } + } + + throw createSyntaxError(state, 'Invalid left hand side of assignment operator ='); + } + + return node; + } + /** + * conditional operation + * + * condition ? truePart : falsePart + * + * Note: conditional operator is right-associative + * + * @return {Node} node + * @private + */ + + + function parseConditional(state) { + var node = parseLogicalOr(state); + + while (state.token === '?') { + // eslint-disable-line no-unmodified-loop-condition + // set a conditional level, the range operator will be ignored as long + // as conditionalLevel === state.nestingLevel. + var prev = state.conditionalLevel; + state.conditionalLevel = state.nestingLevel; + getTokenSkipNewline(state); + var condition = node; + var trueExpr = parseAssignment(state); + if (state.token !== ':') throw createSyntaxError(state, 'False part of conditional expression expected'); + state.conditionalLevel = null; + getTokenSkipNewline(state); + var falseExpr = parseAssignment(state); // Note: check for conditional operator again, right associativity + + node = new ConditionalNode(condition, trueExpr, falseExpr); // restore the previous conditional level + + state.conditionalLevel = prev; + } + + return node; + } + /** + * logical or, 'x or y' + * @return {Node} node + * @private + */ + + + function parseLogicalOr(state) { + var node = parseLogicalXor(state); + + while (state.token === 'or') { + // eslint-disable-line no-unmodified-loop-condition + getTokenSkipNewline(state); + node = new OperatorNode('or', 'or', [node, parseLogicalXor(state)]); + } + + return node; + } + /** + * logical exclusive or, 'x xor y' + * @return {Node} node + * @private + */ + + + function parseLogicalXor(state) { + var node = parseLogicalAnd(state); + + while (state.token === 'xor') { + // eslint-disable-line no-unmodified-loop-condition + getTokenSkipNewline(state); + node = new OperatorNode('xor', 'xor', [node, parseLogicalAnd(state)]); + } + + return node; + } + /** + * logical and, 'x and y' + * @return {Node} node + * @private + */ + + + function parseLogicalAnd(state) { + var node = parseBitwiseOr(state); + + while (state.token === 'and') { + // eslint-disable-line no-unmodified-loop-condition + getTokenSkipNewline(state); + node = new OperatorNode('and', 'and', [node, parseBitwiseOr(state)]); + } + + return node; + } + /** + * bitwise or, 'x | y' + * @return {Node} node + * @private + */ + + + function parseBitwiseOr(state) { + var node = parseBitwiseXor(state); + + while (state.token === '|') { + // eslint-disable-line no-unmodified-loop-condition + getTokenSkipNewline(state); + node = new OperatorNode('|', 'bitOr', [node, parseBitwiseXor(state)]); + } + + return node; + } + /** + * bitwise exclusive or (xor), 'x ^| y' + * @return {Node} node + * @private + */ + + + function parseBitwiseXor(state) { + var node = parseBitwiseAnd(state); + + while (state.token === '^|') { + // eslint-disable-line no-unmodified-loop-condition + getTokenSkipNewline(state); + node = new OperatorNode('^|', 'bitXor', [node, parseBitwiseAnd(state)]); + } + + return node; + } + /** + * bitwise and, 'x & y' + * @return {Node} node + * @private + */ + + + function parseBitwiseAnd(state) { + var node = parseRelational(state); + + while (state.token === '&') { + // eslint-disable-line no-unmodified-loop-condition + getTokenSkipNewline(state); + node = new OperatorNode('&', 'bitAnd', [node, parseRelational(state)]); + } + + return node; + } + /** + * Parse a chained conditional, like 'a > b >= c' + * @return {Node} node + */ + + + function parseRelational(state) { + var params = [parseShift(state)]; + var conditionals = []; + var operators = { + '==': 'equal', + '!=': 'unequal', + '<': 'smaller', + '>': 'larger', + '<=': 'smallerEq', + '>=': 'largerEq' + }; + + while (operators.hasOwnProperty(state.token)) { + // eslint-disable-line no-unmodified-loop-condition + var cond = { + name: state.token, + fn: operators[state.token] + }; + conditionals.push(cond); + getTokenSkipNewline(state); + params.push(parseShift(state)); + } + + if (params.length === 1) { + return params[0]; + } else if (params.length === 2) { + return new OperatorNode(conditionals[0].name, conditionals[0].fn, params); + } else { + return new RelationalNode(conditionals.map(function (c) { + return c.fn; + }), params); + } + } + /** + * Bitwise left shift, bitwise right arithmetic shift, bitwise right logical shift + * @return {Node} node + * @private + */ + + + function parseShift(state) { + var node, operators, name, fn, params; + node = parseConversion(state); + operators = { + '<<': 'leftShift', + '>>': 'rightArithShift', + '>>>': 'rightLogShift' + }; + + while (operators.hasOwnProperty(state.token)) { + name = state.token; + fn = operators[name]; + getTokenSkipNewline(state); + params = [node, parseConversion(state)]; + node = new OperatorNode(name, fn, params); + } + + return node; + } + /** + * conversion operators 'to' and 'in' + * @return {Node} node + * @private + */ + + + function parseConversion(state) { + var node, operators, name, fn, params; + node = parseRange(state); + operators = { + 'to': 'to', + 'in': 'to' // alias of 'to' + + }; + + while (operators.hasOwnProperty(state.token)) { + name = state.token; + fn = operators[name]; + getTokenSkipNewline(state); + + if (name === 'in' && state.token === '') { + // end of expression -> this is the unit 'in' ('inch') + node = new OperatorNode('*', 'multiply', [node, new SymbolNode('in')], true); + } else { + // operator 'a to b' or 'a in b' + params = [node, parseRange(state)]; + node = new OperatorNode(name, fn, params); + } + } + + return node; + } + /** + * parse range, "start:end", "start:step:end", ":", "start:", ":end", etc + * @return {Node} node + * @private + */ + + + function parseRange(state) { + var node; + var params = []; + + if (state.token === ':') { + // implicit start=1 (one-based) + node = new ConstantNode(1); + } else { + // explicit start + node = parseAddSubtract(state); + } + + if (state.token === ':' && state.conditionalLevel !== state.nestingLevel) { + // we ignore the range operator when a conditional operator is being processed on the same level + params.push(node); // parse step and end + + while (state.token === ':' && params.length < 3) { + // eslint-disable-line no-unmodified-loop-condition + getTokenSkipNewline(state); + + if (state.token === ')' || state.token === ']' || state.token === ',' || state.token === '') { + // implicit end + params.push(new SymbolNode('end')); + } else { + // explicit end + params.push(parseAddSubtract(state)); + } + } + + if (params.length === 3) { + // params = [start, step, end] + node = new RangeNode(params[0], params[2], params[1]); // start, end, step + } else { + // length === 2 + // params = [start, end] + node = new RangeNode(params[0], params[1]); // start, end + } + } + + return node; + } + /** + * add or subtract + * @return {Node} node + * @private + */ + + + function parseAddSubtract(state) { + var node, operators, name, fn, params; + node = parseMultiplyDivide(state); + operators = { + '+': 'add', + '-': 'subtract' + }; + + while (operators.hasOwnProperty(state.token)) { + name = state.token; + fn = operators[name]; + getTokenSkipNewline(state); + params = [node, parseMultiplyDivide(state)]; + node = new OperatorNode(name, fn, params); + } + + return node; + } + /** + * multiply, divide, modulus + * @return {Node} node + * @private + */ + + + function parseMultiplyDivide(state) { + var node, last, operators, name, fn; + node = parseImplicitMultiplication(state); + last = node; + operators = { + '*': 'multiply', + '.*': 'dotMultiply', + '/': 'divide', + './': 'dotDivide', + '%': 'mod', + 'mod': 'mod' + }; + + while (true) { + if (operators.hasOwnProperty(state.token)) { + // explicit operators + name = state.token; + fn = operators[name]; + getTokenSkipNewline(state); + last = parseImplicitMultiplication(state); + node = new OperatorNode(name, fn, [node, last]); + } else { + break; + } + } + + return node; + } + /** + * implicit multiplication + * @return {Node} node + * @private + */ + + + function parseImplicitMultiplication(state) { + var node, last; + node = parseRule2(state); + last = node; + + while (true) { + if (state.tokenType === TOKENTYPE.SYMBOL || state.token === 'in' && type.isConstantNode(node) || state.tokenType === TOKENTYPE.NUMBER && !type.isConstantNode(last) && (!type.isOperatorNode(last) || last.op === '!') || state.token === '(') { + // parse implicit multiplication + // + // symbol: implicit multiplication like '2a', '(2+3)a', 'a b' + // number: implicit multiplication like '(2+3)2' + // parenthesis: implicit multiplication like '2(3+4)', '(3+4)(1+2)' + last = parseRule2(state); + node = new OperatorNode('*', 'multiply', [node, last], true + /* implicit */ + ); + } else { + break; + } + } + + return node; + } + /** + * Infamous "rule 2" as described in https://github.com/josdejong/mathjs/issues/792#issuecomment-361065370 + * Explicit division gets higher precedence than implicit multiplication + * when the division matches this pattern: [number] / [number] [symbol] + * @return {Node} node + * @private + */ + + + function parseRule2(state) { + var node = parseUnary(state); + var last = node; + var tokenStates = []; + + while (true) { + // Match the "number /" part of the pattern "number / number symbol" + if (state.token === '/' && type.isConstantNode(last)) { + // Look ahead to see if the next token is a number + tokenStates.push(_extends({}, state)); + getTokenSkipNewline(state); // Match the "number / number" part of the pattern + + if (state.tokenType === TOKENTYPE.NUMBER) { + // Look ahead again + tokenStates.push(_extends({}, state)); + getTokenSkipNewline(state); // Match the "symbol" part of the pattern, or a left parenthesis + + if (state.tokenType === TOKENTYPE.SYMBOL || state.token === '(') { + // We've matched the pattern "number / number symbol". + // Rewind once and build the "number / number" node; the symbol will be consumed later + _extends(state, tokenStates.pop()); + + tokenStates.pop(); + last = parseUnary(state); + node = new OperatorNode('/', 'divide', [node, last]); + } else { + // Not a match, so rewind + tokenStates.pop(); + + _extends(state, tokenStates.pop()); + + break; + } + } else { + // Not a match, so rewind + _extends(state, tokenStates.pop()); + + break; + } + } else { + break; + } + } + + return node; + } + /** + * Unary plus and minus, and logical and bitwise not + * @return {Node} node + * @private + */ + + + function parseUnary(state) { + var name, params, fn; + var operators = { + '-': 'unaryMinus', + '+': 'unaryPlus', + '~': 'bitNot', + 'not': 'not' + }; + + if (operators.hasOwnProperty(state.token)) { + fn = operators[state.token]; + name = state.token; + getTokenSkipNewline(state); + params = [parseUnary(state)]; + return new OperatorNode(name, fn, params); + } + + return parsePow(state); + } + /** + * power + * Note: power operator is right associative + * @return {Node} node + * @private + */ + + + function parsePow(state) { + var node, name, fn, params; + node = parseLeftHandOperators(state); + + if (state.token === '^' || state.token === '.^') { + name = state.token; + fn = name === '^' ? 'pow' : 'dotPow'; + getTokenSkipNewline(state); + params = [node, parseUnary(state)]; // Go back to unary, we can have '2^-3' + + node = new OperatorNode(name, fn, params); + } + + return node; + } + /** + * Left hand operators: factorial x!, ctranspose x' + * @return {Node} node + * @private + */ + + + function parseLeftHandOperators(state) { + var node, operators, name, fn, params; + node = parseCustomNodes(state); + operators = { + '!': 'factorial', + '\'': 'ctranspose' + }; + + while (operators.hasOwnProperty(state.token)) { + name = state.token; + fn = operators[name]; + getToken(state); + params = [node]; + node = new OperatorNode(name, fn, params); + node = parseAccessors(state, node); + } + + return node; + } + /** + * Parse a custom node handler. A node handler can be used to process + * nodes in a custom way, for example for handling a plot. + * + * A handler must be passed as second argument of the parse function. + * - must extend math.expression.node.Node + * - must contain a function _compile(defs: Object) : string + * - must contain a function find(filter: Object) : Node[] + * - must contain a function toString() : string + * - the constructor is called with a single argument containing all parameters + * + * For example: + * + * nodes = { + * 'plot': PlotHandler + * } + * + * The constructor of the handler is called as: + * + * node = new PlotHandler(params) + * + * The handler will be invoked when evaluating an expression like: + * + * node = math.parse('plot(sin(x), x)', nodes) + * + * @return {Node} node + * @private + */ + + + function parseCustomNodes(state) { + var params = []; + + if (state.tokenType === TOKENTYPE.SYMBOL && state.extraNodes.hasOwnProperty(state.token)) { + var CustomNode = state.extraNodes[state.token]; + getToken(state); // parse parameters + + if (state.token === '(') { + params = []; + openParams(state); + getToken(state); + + if (state.token !== ')') { + params.push(parseAssignment(state)); // parse a list with parameters + + while (state.token === ',') { + // eslint-disable-line no-unmodified-loop-condition + getToken(state); + params.push(parseAssignment(state)); + } + } + + if (state.token !== ')') { + throw createSyntaxError(state, 'Parenthesis ) expected'); + } + + closeParams(state); + getToken(state); + } // create a new custom node + // noinspection JSValidateTypes + + + return new CustomNode(params); + } + + return parseSymbol(state); + } + /** + * parse symbols: functions, variables, constants, units + * @return {Node} node + * @private + */ + + + function parseSymbol(state) { + var node, name; + + if (state.tokenType === TOKENTYPE.SYMBOL || state.tokenType === TOKENTYPE.DELIMITER && state.token in NAMED_DELIMITERS) { + name = state.token; + getToken(state); + + if (CONSTANTS.hasOwnProperty(name)) { + // true, false, null, ... + node = new ConstantNode(CONSTANTS[name]); + } else if (NUMERIC_CONSTANTS.indexOf(name) !== -1) { + // NaN, Infinity + node = new ConstantNode(numeric(name, 'number')); + } else { + node = new SymbolNode(name); + } // parse function parameters and matrix index + + + node = parseAccessors(state, node); + return node; + } + + return parseDoubleQuotesString(state); + } + /** + * parse accessors: + * - function invocation in round brackets (...), for example sqrt(2) + * - index enclosed in square brackets [...], for example A[2,3] + * - dot notation for properties, like foo.bar + * @param {Node} node Node on which to apply the parameters. If there + * are no parameters in the expression, the node + * itself is returned + * @param {string[]} [types] Filter the types of notations + * can be ['(', '[', '.'] + * @return {Node} node + * @private + */ + + + function parseAccessors(state, node, types) { + var params; + + while ((state.token === '(' || state.token === '[' || state.token === '.') && (!types || types.indexOf(state.token) !== -1)) { + // eslint-disable-line no-unmodified-loop-condition + params = []; + + if (state.token === '(') { + if (type.isSymbolNode(node) || type.isAccessorNode(node)) { + // function invocation like fn(2, 3) or obj.fn(2, 3) + openParams(state); + getToken(state); + + if (state.token !== ')') { + params.push(parseAssignment(state)); // parse a list with parameters + + while (state.token === ',') { + // eslint-disable-line no-unmodified-loop-condition + getToken(state); + params.push(parseAssignment(state)); + } + } + + if (state.token !== ')') { + throw createSyntaxError(state, 'Parenthesis ) expected'); + } + + closeParams(state); + getToken(state); + node = new FunctionNode(node, params); + } else { + // implicit multiplication like (2+3)(4+5) or sqrt(2)(1+2) + // don't parse it here but let it be handled by parseImplicitMultiplication + // with correct precedence + return node; + } + } else if (state.token === '[') { + // index notation like variable[2, 3] + openParams(state); + getToken(state); + + if (state.token !== ']') { + params.push(parseAssignment(state)); // parse a list with parameters + + while (state.token === ',') { + // eslint-disable-line no-unmodified-loop-condition + getToken(state); + params.push(parseAssignment(state)); + } + } + + if (state.token !== ']') { + throw createSyntaxError(state, 'Parenthesis ] expected'); + } + + closeParams(state); + getToken(state); + node = new AccessorNode(node, new IndexNode(params)); + } else { + // dot notation like variable.prop + getToken(state); + + if (state.tokenType !== TOKENTYPE.SYMBOL) { + throw createSyntaxError(state, 'Property name expected after dot'); + } + + params.push(new ConstantNode(state.token)); + getToken(state); + var dotNotation = true; + node = new AccessorNode(node, new IndexNode(params, dotNotation)); + } + } + + return node; + } + /** + * Parse a double quotes string. + * @return {Node} node + * @private + */ + + + function parseDoubleQuotesString(state) { + var node, str; + + if (state.token === '"') { + str = parseDoubleQuotesStringToken(state); // create constant + + node = new ConstantNode(str); // parse index parameters + + node = parseAccessors(state, node); + return node; + } + + return parseSingleQuotesString(state); + } + /** + * Parse a string surrounded by double quotes "..." + * @return {string} + */ + + + function parseDoubleQuotesStringToken(state) { + var str = ''; + + while (currentCharacter(state) !== '' && currentCharacter(state) !== '"') { + if (currentCharacter(state) === '\\') { + // escape character, immediately process the next + // character to prevent stopping at a next '\"' + str += currentCharacter(state); + next(state); + } + + str += currentCharacter(state); + next(state); + } + + getToken(state); + + if (state.token !== '"') { + throw createSyntaxError(state, 'End of string " expected'); + } + + getToken(state); + return JSON.parse('"' + str + '"'); // unescape escaped characters + } + /** + * Parse a single quotes string. + * @return {Node} node + * @private + */ + + + function parseSingleQuotesString(state) { + var node, str; + + if (state.token === '\'') { + str = parseSingleQuotesStringToken(state); // create constant + + node = new ConstantNode(str); // parse index parameters + + node = parseAccessors(state, node); + return node; + } + + return parseMatrix(state); + } + /** + * Parse a string surrounded by single quotes '...' + * @return {string} + */ + + + function parseSingleQuotesStringToken(state) { + var str = ''; + + while (currentCharacter(state) !== '' && currentCharacter(state) !== '\'') { + if (currentCharacter(state) === '\\') { + // escape character, immediately process the next + // character to prevent stopping at a next '\'' + str += currentCharacter(state); + next(state); + } + + str += currentCharacter(state); + next(state); + } + + getToken(state); + + if (state.token !== '\'') { + throw createSyntaxError(state, 'End of string \' expected'); + } + + getToken(state); + return JSON.parse('"' + str + '"'); // unescape escaped characters + } + /** + * parse the matrix + * @return {Node} node + * @private + */ + + + function parseMatrix(state) { + var array, params, rows, cols; + + if (state.token === '[') { + // matrix [...] + openParams(state); + getToken(state); + + if (state.token !== ']') { + // this is a non-empty matrix + var row = parseRow(state); + + if (state.token === ';') { + // 2 dimensional array + rows = 1; + params = [row]; // the rows of the matrix are separated by dot-comma's + + while (state.token === ';') { + // eslint-disable-line no-unmodified-loop-condition + getToken(state); + params[rows] = parseRow(state); + rows++; + } + + if (state.token !== ']') { + throw createSyntaxError(state, 'End of matrix ] expected'); + } + + closeParams(state); + getToken(state); // check if the number of columns matches in all rows + + cols = params[0].items.length; + + for (var r = 1; r < rows; r++) { + if (params[r].items.length !== cols) { + throw createError(state, 'Column dimensions mismatch ' + '(' + params[r].items.length + ' !== ' + cols + ')'); + } + } + + array = new ArrayNode(params); + } else { + // 1 dimensional vector + if (state.token !== ']') { + throw createSyntaxError(state, 'End of matrix ] expected'); + } + + closeParams(state); + getToken(state); + array = row; + } + } else { + // this is an empty matrix "[ ]" + closeParams(state); + getToken(state); + array = new ArrayNode([]); + } + + return parseAccessors(state, array); + } + + return parseObject(state); + } + /** + * Parse a single comma-separated row from a matrix, like 'a, b, c' + * @return {ArrayNode} node + */ + + + function parseRow(state) { + var params = [parseAssignment(state)]; + var len = 1; + + while (state.token === ',') { + // eslint-disable-line no-unmodified-loop-condition + getToken(state); // parse expression + + params[len] = parseAssignment(state); + len++; + } + + return new ArrayNode(params); + } + /** + * parse an object, enclosed in angle brackets{...}, for example {value: 2} + * @return {Node} node + * @private + */ + + + function parseObject(state) { + if (state.token === '{') { + var key; + var properties = {}; + + do { + getToken(state); + + if (state.token !== '}') { + // parse key + if (state.token === '"') { + key = parseDoubleQuotesStringToken(state); + } else if (state.token === '\'') { + key = parseSingleQuotesStringToken(state); + } else if (state.tokenType === TOKENTYPE.SYMBOL) { + key = state.token; + getToken(state); + } else { + throw createSyntaxError(state, 'Symbol or string expected as object key'); + } // parse key/value separator + + + if (state.token !== ':') { + throw createSyntaxError(state, 'Colon : expected after object key'); + } + + getToken(state); // parse key + + properties[key] = parseAssignment(state); + } + } while (state.token === ','); // eslint-disable-line no-unmodified-loop-condition + + + if (state.token !== '}') { + throw createSyntaxError(state, 'Comma , or bracket } expected after object value'); + } + + getToken(state); + var node = new ObjectNode(properties); // parse index parameters + + node = parseAccessors(state, node); + return node; + } + + return parseNumber(state); + } + /** + * parse a number + * @return {Node} node + * @private + */ + + + function parseNumber(state) { + var numberStr; + + if (state.tokenType === TOKENTYPE.NUMBER) { + // this is a number + numberStr = state.token; + getToken(state); + return new ConstantNode(numeric(numberStr, config.number)); + } + + return parseParentheses(state); + } + /** + * parentheses + * @return {Node} node + * @private + */ + + + function parseParentheses(state) { + var node; // check if it is a parenthesized expression + + if (state.token === '(') { + // parentheses (...) + openParams(state); + getToken(state); + node = parseAssignment(state); // start again + + if (state.token !== ')') { + throw createSyntaxError(state, 'Parenthesis ) expected'); + } + + closeParams(state); + getToken(state); + node = new ParenthesisNode(node); + node = parseAccessors(state, node); + return node; + } + + return parseEnd(state); + } + /** + * Evaluated when the expression is not yet ended but expected to end + * @return {Node} res + * @private + */ + + + function parseEnd(state) { + if (state.token === '') { + // syntax error or unexpected end of expression + throw createSyntaxError(state, 'Unexpected end of expression'); + } else { + throw createSyntaxError(state, 'Value expected'); + } + } + /** + * Shortcut for getting the current row value (one based) + * Returns the line of the currently handled expression + * @private + */ + + /* TODO: implement keeping track on the row number + function row () { + return null + } + */ + + /** + * Shortcut for getting the current col value (one based) + * Returns the column (position) where the last state.token starts + * @private + */ + + + function col(state) { + return state.index - state.token.length + 1; + } + /** + * Create an error + * @param {string} message + * @return {SyntaxError} instantiated error + * @private + */ + + + function createSyntaxError(state, message) { + var c = col(state); + var error = new SyntaxError(message + ' (char ' + c + ')'); + error['char'] = c; + return error; + } + /** + * Create an error + * @param {string} message + * @return {Error} instantiated error + * @private + */ + + + function createError(state, message) { + var c = col(state); + var error = new SyntaxError(message + ' (char ' + c + ')'); + error['char'] = c; + return error; + } + + return parse; +} + +exports.name = 'parse'; +exports.path = 'expression'; +exports.factory = factory; +},{"../error/ArgumentsError":8,"../type/numeric":44,"../utils/collection/deepMap":49,"./node/AccessorNode":15,"./node/ArrayNode":16,"./node/AssignmentNode":17,"./node/BlockNode":18,"./node/ConditionalNode":19,"./node/ConstantNode":20,"./node/FunctionAssignmentNode":21,"./node/FunctionNode":22,"./node/IndexNode":23,"./node/ObjectNode":25,"./node/OperatorNode":26,"./node/ParenthesisNode":27,"./node/RangeNode":28,"./node/RelationalNode":29,"./node/SymbolNode":30}],35:[function(require,module,exports){ +'use strict'; + +var IndexError = require('../../error/IndexError'); +/** + * Transform zero-based indices to one-based indices in errors + * @param {Error} err + * @returns {Error} Returns the transformed error + */ + + +exports.transform = function (err) { + if (err && err.isIndexError) { + return new IndexError(err.index + 1, err.min + 1, err.max !== undefined ? err.max + 1 : undefined); + } + + return err; +}; +},{"../../error/IndexError":10}],36:[function(require,module,exports){ +'use strict'; + +var clone = require('../../utils/object').clone; + +var validateIndex = require('../../utils/array').validateIndex; + +var getSafeProperty = require('../../utils/customs').getSafeProperty; + +var setSafeProperty = require('../../utils/customs').setSafeProperty; + +var DimensionError = require('../../error/DimensionError'); + +function factory(type, config, load, typed) { + var matrix = load(require('../../type/matrix/function/matrix')); + /** + * Get or set a subset of a matrix or string. + * + * Syntax: + * math.subset(value, index) // retrieve a subset + * math.subset(value, index, replacement [, defaultValue]) // replace a subset + * + * Examples: + * + * // get a subset + * const d = [[1, 2], [3, 4]] + * math.subset(d, math.index(1, 0)) // returns 3 + * math.subset(d, math.index([0, 1], 1)) // returns [[2], [4]] + * + * // replace a subset + * const e = [] + * const f = math.subset(e, math.index(0, [0, 2]), [5, 6]) // f = [[5, 6]] + * const g = math.subset(f, math.index(1, 1), 7, 0) // g = [[5, 6], [0, 7]] + * + * See also: + * + * size, resize, squeeze, index + * + * @param {Array | Matrix | string} matrix An array, matrix, or string + * @param {Index} index An index containing ranges for each + * dimension + * @param {*} [replacement] An array, matrix, or scalar. + * If provided, the subset is replaced with replacement. + * If not provided, the subset is returned + * @param {*} [defaultValue=undefined] Default value, filled in on new entries when + * the matrix is resized. If not provided, + * math.matrix elements will be left undefined. + * @return {Array | Matrix | string} Either the retrieved subset or the updated matrix. + */ + + var subset = typed('subset', { + // get subset + 'Array, Index': function ArrayIndex(value, index) { + var m = matrix(value); + var subset = m.subset(index); // returns a Matrix + + return index.isScalar() ? subset : subset.valueOf(); // return an Array (like the input) + }, + 'Matrix, Index': function MatrixIndex(value, index) { + return value.subset(index); + }, + 'Object, Index': _getObjectProperty, + 'string, Index': _getSubstring, + // set subset + 'Array, Index, any': function ArrayIndexAny(value, index, replacement) { + return matrix(clone(value)).subset(index, replacement, undefined).valueOf(); + }, + 'Array, Index, any, any': function ArrayIndexAnyAny(value, index, replacement, defaultValue) { + return matrix(clone(value)).subset(index, replacement, defaultValue).valueOf(); + }, + 'Matrix, Index, any': function MatrixIndexAny(value, index, replacement) { + return value.clone().subset(index, replacement); + }, + 'Matrix, Index, any, any': function MatrixIndexAnyAny(value, index, replacement, defaultValue) { + return value.clone().subset(index, replacement, defaultValue); + }, + 'string, Index, string': _setSubstring, + 'string, Index, string, string': _setSubstring, + 'Object, Index, any': _setObjectProperty + }); + subset.toTex = undefined; // use default template + + return subset; + /** + * Retrieve a subset of a string + * @param {string} str string from which to get a substring + * @param {Index} index An index containing ranges for each dimension + * @returns {string} substring + * @private + */ + + function _getSubstring(str, index) { + if (!type.isIndex(index)) { + // TODO: better error message + throw new TypeError('Index expected'); + } + + if (index.size().length !== 1) { + throw new DimensionError(index.size().length, 1); + } // validate whether the range is out of range + + + var strLen = str.length; + validateIndex(index.min()[0], strLen); + validateIndex(index.max()[0], strLen); + var range = index.dimension(0); + var substr = ''; + range.forEach(function (v) { + substr += str.charAt(v); + }); + return substr; + } + /** + * Replace a substring in a string + * @param {string} str string to be replaced + * @param {Index} index An index containing ranges for each dimension + * @param {string} replacement Replacement string + * @param {string} [defaultValue] Default value to be uses when resizing + * the string. is ' ' by default + * @returns {string} result + * @private + */ + + + function _setSubstring(str, index, replacement, defaultValue) { + if (!index || index.isIndex !== true) { + // TODO: better error message + throw new TypeError('Index expected'); + } + + if (index.size().length !== 1) { + throw new DimensionError(index.size().length, 1); + } + + if (defaultValue !== undefined) { + if (typeof defaultValue !== 'string' || defaultValue.length !== 1) { + throw new TypeError('Single character expected as defaultValue'); + } + } else { + defaultValue = ' '; + } + + var range = index.dimension(0); + var len = range.size()[0]; + + if (len !== replacement.length) { + throw new DimensionError(range.size()[0], replacement.length); + } // validate whether the range is out of range + + + var strLen = str.length; + validateIndex(index.min()[0]); + validateIndex(index.max()[0]); // copy the string into an array with characters + + var chars = []; + + for (var i = 0; i < strLen; i++) { + chars[i] = str.charAt(i); + } + + range.forEach(function (v, i) { + chars[v] = replacement.charAt(i[0]); + }); // initialize undefined characters with a space + + if (chars.length > strLen) { + for (var _i = strLen - 1, _len = chars.length; _i < _len; _i++) { + if (!chars[_i]) { + chars[_i] = defaultValue; + } + } + } + + return chars.join(''); + } +} +/** + * Retrieve a property from an object + * @param {Object} object + * @param {Index} index + * @return {*} Returns the value of the property + * @private + */ + + +function _getObjectProperty(object, index) { + if (index.size().length !== 1) { + throw new DimensionError(index.size(), 1); + } + + var key = index.dimension(0); + + if (typeof key !== 'string') { + throw new TypeError('String expected as index to retrieve an object property'); + } + + return getSafeProperty(object, key); +} +/** + * Set a property on an object + * @param {Object} object + * @param {Index} index + * @param {*} replacement + * @return {*} Returns the updated object + * @private + */ + + +function _setObjectProperty(object, index, replacement) { + if (index.size().length !== 1) { + throw new DimensionError(index.size(), 1); + } + + var key = index.dimension(0); + + if (typeof key !== 'string') { + throw new TypeError('String expected as index to retrieve an object property'); + } // clone the object, and apply the property to the clone + + + var updated = clone(object); + setSafeProperty(updated, key, replacement); + return updated; +} + +exports.name = 'subset'; +exports.factory = factory; +},{"../../error/DimensionError":9,"../../type/matrix/function/matrix":42,"../../utils/array":46,"../../utils/customs":51,"../../utils/object":55}],37:[function(require,module,exports){ +'use strict'; + +var string = require('../../utils/string'); + +function factory(type, config, load, typed) { + /** + * Format a value of any type into a string. + * + * Syntax: + * + * math.format(value) + * math.format(value, options) + * math.format(value, precision) + * math.format(value, callback) + * + * Where: + * + * - `value: *` + * The value to be formatted + * - `options: Object` + * An object with formatting options. Available options: + * - `notation: string` + * Number notation. Choose from: + * - 'fixed' + * Always use regular number notation. + * For example '123.40' and '14000000' + * - 'exponential' + * Always use exponential notation. + * For example '1.234e+2' and '1.4e+7' + * - 'engineering' + * Always use engineering notation. + * For example '123.4e+0' and '14.0e+6' + * - 'auto' (default) + * Regular number notation for numbers having an absolute value between + * `lower` and `upper` bounds, and uses exponential notation elsewhere. + * Lower bound is included, upper bound is excluded. + * For example '123.4' and '1.4e7'. + * - `precision: number` + * A number between 0 and 16 to round the digits of the number. In case + * of notations 'exponential', 'engineering', and 'auto', `precision` + * defines the total number of significant digits returned. + * In case of notation 'fixed', `precision` defines the number of + * significant digits after the decimal point. + * `precision` is undefined by default. + * - `lowerExp: number` + * Exponent determining the lower boundary for formatting a value with + * an exponent when `notation='auto`. Default value is `-3`. + * - `upperExp: number` + * Exponent determining the upper boundary for formatting a value with + * an exponent when `notation='auto`. Default value is `5`. + * - `fraction: string`. Available values: 'ratio' (default) or 'decimal'. + * For example `format(fraction(1, 3))` will output '1/3' when 'ratio' is + * configured, and will output `0.(3)` when 'decimal' is configured. + * - `callback: function` + * A custom formatting function, invoked for all numeric elements in `value`, + * for example all elements of a matrix, or the real and imaginary + * parts of a complex number. This callback can be used to override the + * built-in numeric notation with any type of formatting. Function `callback` + * is called with `value` as parameter and must return a string. + * + * When `value` is an Object: + * + * - When the object contains a property `format` being a function, this function + * is invoked as `value.format(options)` and the result is returned. + * - When the object has its own `toString` method, this method is invoked + * and the result is returned. + * - In other cases the function will loop over all object properties and + * return JSON object notation like '{"a": 2, "b": 3}'. + * + * When value is a function: + * + * - When the function has a property `syntax`, it returns this + * syntax description. + * - In other cases, a string `'function'` is returned. + * + * Examples: + * + * math.format(6.4) // returns '6.4' + * math.format(1240000) // returns '1.24e6' + * math.format(1/3) // returns '0.3333333333333333' + * math.format(1/3, 3) // returns '0.333' + * math.format(21385, 2) // returns '21000' + * math.format(12e8, {notation: 'fixed'}) // returns '1200000000' + * math.format(2.3, {notation: 'fixed', precision: 4}) // returns '2.3000' + * math.format(52.8, {notation: 'exponential'}) // returns '5.28e+1' + * math.format(12400,{notation: 'engineering'}) // returns '12.400e+3' + * math.format(2000, {lowerExp: -2, upperExp: 2}) // returns '2e+3' + * + * function formatCurrency(value) { + * // return currency notation with two digits: + * return '$' + value.toFixed(2) + * + * // you could also use math.format inside the callback: + * // return '$' + math.format(value, {notation: 'fixed', precision: 2}) + * } + * math.format([2.1, 3, 0.016], formatCurrency} // returns '[$2.10, $3.00, $0.02]' + * + * See also: + * + * print + * + * @param {*} value Value to be stringified + * @param {Object | Function | number} [options] Formatting options + * @return {string} The formatted value + */ + var format = typed('format', { + 'any': string.format, + 'any, Object | function | number': string.format + }); + format.toTex = undefined; // use default template + + return format; +} + +exports.name = 'format'; +exports.factory = factory; +},{"../../utils/string":57}],38:[function(require,module,exports){ +'use strict'; + +function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); } + +function factory(type, config, load, typed) { + /** + * Determine the type of a variable. + * + * Function `typeof` recognizes the following types of objects: + * + * Object | Returns | Example + * ---------------------- | ------------- | ------------------------------------------ + * null | `'null'` | `math.typeof(null)` + * number | `'number'` | `math.typeof(3.5)` + * boolean | `'boolean'` | `math.typeof(true)` + * string | `'string'` | `math.typeof('hello world')` + * Array | `'Array'` | `math.typeof([1, 2, 3])` + * Date | `'Date'` | `math.typeof(new Date())` + * Function | `'Function'` | `math.typeof(function () {})` + * Object | `'Object'` | `math.typeof({a: 2, b: 3})` + * RegExp | `'RegExp'` | `math.typeof(/a regexp/)` + * undefined | `'undefined'` | `math.typeof(undefined)` + * math.type.BigNumber | `'BigNumber'` | `math.typeof(math.bignumber('2.3e500'))` + * math.type.Chain | `'Chain'` | `math.typeof(math.chain(2))` + * math.type.Complex | `'Complex'` | `math.typeof(math.complex(2, 3))` + * math.type.Fraction | `'Fraction'` | `math.typeof(math.fraction(1, 3))` + * math.type.Help | `'Help'` | `math.typeof(math.help('sqrt'))` + * math.type.Help | `'Help'` | `math.typeof(math.help('sqrt'))` + * math.type.Index | `'Index'` | `math.typeof(math.index(1, 3))` + * math.type.Matrix | `'Matrix'` | `math.typeof(math.matrix([[1,2], [3, 4]]))` + * math.type.Range | `'Range'` | `math.typeof(math.range(0, 10))` + * math.type.ResultSet | `'ResultSet'` | `math.typeof(math.eval('a=2\nb=3'))` + * math.type.Unit | `'Unit'` | `math.typeof(math.unit('45 deg'))` + * math.expression.node​.AccessorNode | `'AccessorNode'` | `math.typeof(math.parse('A[2]'))` + * math.expression.node​.ArrayNode | `'ArrayNode'` | `math.typeof(math.parse('[1,2,3]'))` + * math.expression.node​.AssignmentNode | `'AssignmentNode'` | `math.typeof(math.parse('x=2'))` + * math.expression.node​.BlockNode | `'BlockNode'` | `math.typeof(math.parse('a=2; b=3'))` + * math.expression.node​.ConditionalNode | `'ConditionalNode'` | `math.typeof(math.parse('x<0 ? -x : x'))` + * math.expression.node​.ConstantNode | `'ConstantNode'` | `math.typeof(math.parse('2.3'))` + * math.expression.node​.FunctionAssignmentNode | `'FunctionAssignmentNode'` | `math.typeof(math.parse('f(x)=x^2'))` + * math.expression.node​.FunctionNode | `'FunctionNode'` | `math.typeof(math.parse('sqrt(4)'))` + * math.expression.node​.IndexNode | `'IndexNode'` | `math.typeof(math.parse('A[2]').index)` + * math.expression.node​.ObjectNode | `'ObjectNode'` | `math.typeof(math.parse('{a:2}'))` + * math.expression.node​.ParenthesisNode | `'ParenthesisNode'` | `math.typeof(math.parse('(2+3)'))` + * math.expression.node​.RangeNode | `'RangeNode'` | `math.typeof(math.parse('1:10'))` + * math.expression.node​.SymbolNode | `'SymbolNode'` | `math.typeof(math.parse('x'))` + * + * Syntax: + * + * math.typeof(x) + * + * Examples: + * + * math.typeof(3.5) // returns 'number' + * math.typeof(math.complex('2-4i')) // returns 'Complex' + * math.typeof(math.unit('45 deg')) // returns 'Unit' + * math.typeof('hello world') // returns 'string' + * + * @param {*} x The variable for which to test the type. + * @return {string} Returns the name of the type. Primitive types are lower case, + * non-primitive types are upper-camel-case. + * For example 'number', 'string', 'Array', 'Date'. + */ + var _typeof = typed('_typeof', { + 'any': function any(x) { + var t = _typeof2(x); + + if (t === 'object') { + // JavaScript types + if (x === null) return 'null'; + if (Array.isArray(x)) return 'Array'; + if (x instanceof Date) return 'Date'; + if (x instanceof RegExp) return 'RegExp'; // math.js types + + if (type.isBigNumber(x)) return 'BigNumber'; + if (type.isComplex(x)) return 'Complex'; + if (type.isFraction(x)) return 'Fraction'; + if (type.isMatrix(x)) return 'Matrix'; + if (type.isUnit(x)) return 'Unit'; + if (type.isIndex(x)) return 'Index'; + if (type.isRange(x)) return 'Range'; + if (type.isResultSet(x)) return 'ResultSet'; + if (type.isNode(x)) return x.type; + if (type.isChain(x)) return 'Chain'; + if (type.isHelp(x)) return 'Help'; + return 'Object'; + } + + if (t === 'function') return 'Function'; + return t; // can be 'string', 'number', 'boolean', ... + } + }); + + _typeof.toTex = undefined; // use default template + + return _typeof; +} + +exports.name = 'typeof'; +exports.factory = factory; +},{}],39:[function(require,module,exports){ +'use strict'; + +var deepMap = require('../../../utils/collection/deepMap'); + +function factory(type, config, load, typed) { + /** + * Create a BigNumber, which can store numbers with arbitrary precision. + * When a matrix is provided, all elements will be converted to BigNumber. + * + * Syntax: + * + * math.bignumber(x) + * + * Examples: + * + * 0.1 + 0.2 // returns number 0.30000000000000004 + * math.bignumber(0.1) + math.bignumber(0.2) // returns BigNumber 0.3 + * + * + * 7.2e500 // returns number Infinity + * math.bignumber('7.2e500') // returns BigNumber 7.2e500 + * + * See also: + * + * boolean, complex, index, matrix, string, unit + * + * @param {number | string | Fraction | BigNumber | Array | Matrix | boolean | null} [value] Value for the big number, + * 0 by default. + * @returns {BigNumber} The created bignumber + */ + var bignumber = typed('bignumber', { + '': function _() { + return new type.BigNumber(0); + }, + 'number': function number(x) { + // convert to string to prevent errors in case of >15 digits + return new type.BigNumber(x + ''); + }, + 'string': function string(x) { + return new type.BigNumber(x); + }, + 'BigNumber': function BigNumber(x) { + // we assume a BigNumber is immutable + return x; + }, + 'Fraction': function Fraction(x) { + return new type.BigNumber(x.n).div(x.d).times(x.s); + }, + 'null': function _null(x) { + return new type.BigNumber(0); + }, + 'Array | Matrix': function ArrayMatrix(x) { + return deepMap(x, bignumber); + } + }); + bignumber.toTex = { + 0: '0', + 1: "\\left(${args[0]}\\right)" + }; + return bignumber; +} + +exports.name = 'bignumber'; +exports.factory = factory; +},{"../../../utils/collection/deepMap":49}],40:[function(require,module,exports){ +'use strict'; + +var deepMap = require('../../../utils/collection/deepMap'); + +function factory(type, config, load, typed) { + /** + * Create a fraction convert a value to a fraction. + * + * Syntax: + * math.fraction(numerator, denominator) + * math.fraction({n: numerator, d: denominator}) + * math.fraction(matrix: Array | Matrix) Turn all matrix entries + * into fractions + * + * Examples: + * + * math.fraction(1, 3) + * math.fraction('2/3') + * math.fraction({n: 2, d: 3}) + * math.fraction([0.2, 0.25, 1.25]) + * + * See also: + * + * bignumber, number, string, unit + * + * @param {number | string | Fraction | BigNumber | Array | Matrix} [args] + * Arguments specifying the numerator and denominator of + * the fraction + * @return {Fraction | Array | Matrix} Returns a fraction + */ + var fraction = typed('fraction', { + 'number': function number(x) { + if (!isFinite(x) || isNaN(x)) { + throw new Error(x + ' cannot be represented as a fraction'); + } + + return new type.Fraction(x); + }, + 'string': function string(x) { + return new type.Fraction(x); + }, + 'number, number': function numberNumber(numerator, denominator) { + return new type.Fraction(numerator, denominator); + }, + 'null': function _null(x) { + return new type.Fraction(0); + }, + 'BigNumber': function BigNumber(x) { + return new type.Fraction(x.toString()); + }, + 'Fraction': function Fraction(x) { + return x; // fractions are immutable + }, + 'Object': function Object(x) { + return new type.Fraction(x); + }, + 'Array | Matrix': function ArrayMatrix(x) { + return deepMap(x, fraction); + } + }); + return fraction; +} + +exports.name = 'fraction'; +exports.factory = factory; +},{"../../../utils/collection/deepMap":49}],41:[function(require,module,exports){ +'use strict'; + +var number = require('../../utils/number'); + +function factory(type, config, load, typed) { + /** + * 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: + * + * const range = new Range(start, end) + * const 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: + * + * const c = new Range(2, 6) // 2:1:5 + * c.toArray() // [2, 3, 4, 5] + * const d = new Range(2, -3, -1) // 2:-1:-2 + * d.toArray() // [2, 1, 0, -1, -2] + * + * @class Range + * @constructor Range + * @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'); + } + + var hasStart = start !== null && start !== undefined; + var hasEnd = end !== null && end !== undefined; + var hasStep = step !== null && step !== undefined; + + if (hasStart) { + if (type.isBigNumber(start)) { + start = start.toNumber(); + } else if (typeof start !== 'number') { + throw new TypeError('Parameter start must be a number'); + } + } + + if (hasEnd) { + if (type.isBigNumber(end)) { + end = end.toNumber(); + } else if (typeof end !== 'number') { + throw new TypeError('Parameter end must be a number'); + } + } + + if (hasStep) { + if (type.isBigNumber(step)) { + step = step.toNumber(); + } else if (typeof step !== 'number') { + throw new TypeError('Parameter step must be a number'); + } + } + + this.start = hasStart ? parseFloat(start) : 0; + this.end = hasEnd ? parseFloat(end) : 0; + this.step = hasStep ? parseFloat(step) : 1; + } + /** + * Attach type information + */ + + + Range.prototype.type = 'Range'; + Range.prototype.isRange = true; + /** + * 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'. + * @memberof Range + * @param {string} str + * @return {Range | null} range + */ + + Range.parse = function (str) { + if (typeof str !== 'string') { + 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 () { + return new Range(this.start, this.end, this.step); + }; + /** + * Retrieve the size of the range. + * Returns an array containing one number, the number of elements in the range. + * @memberof Range + * @returns {number[]} size + */ + + + Range.prototype.size = function () { + var len = 0; + var start = this.start; + var step = this.step; + var end = this.end; + var 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 + * @memberof Range + * @return {number | undefined} min + */ + + + Range.prototype.min = function () { + 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 + * @memberof Range + * @return {number | undefined} max + */ + + + Range.prototype.max = function () { + 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. + * @memberof Range + * @param {function} callback The callback method is invoked with three + * parameters: the value of the element, the index + * of the element, and the Range being traversed. + */ + + + Range.prototype.forEach = function (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 + * @memberof 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. + * @returns {Array} array + */ + + + Range.prototype.map = function (callback) { + var array = []; + this.forEach(function (value, index, obj) { + array[index[0]] = callback(value, index, obj); + }); + return array; + }; + /** + * Create an Array with a copy of the Ranges data + * @memberof Range + * @returns {Array} array + */ + + + Range.prototype.toArray = function () { + var array = []; + this.forEach(function (value, index) { + array[index[0]] = value; + }); + return array; + }; + /** + * Get the primitive value of the Range, a one dimensional array + * @memberof Range + * @returns {Array} array + */ + + + Range.prototype.valueOf = function () { + // 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' + * @memberof Range + * @param {Object | number | function} [options] Formatting options. See + * lib/utils/number:format for a + * description of the available + * options. + * @returns {string} str + */ + + + Range.prototype.format = function (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. + * @memberof Range + * @returns {string} + */ + + + Range.prototype.toString = function () { + return this.format(); + }; + /** + * Get a JSON representation of the range + * @memberof Range + * @returns {Object} Returns a JSON object structured as: + * `{"mathjs": "Range", "start": 2, "end": 4, "step": 1}` + */ + + + Range.prototype.toJSON = function () { + return { + mathjs: 'Range', + start: this.start, + end: this.end, + step: this.step + }; + }; + /** + * Instantiate a Range from a JSON object + * @memberof Range + * @param {Object} json A JSON object structured as: + * `{"mathjs": "Range", "start": 2, "end": 4, "step": 1}` + * @return {Range} + */ + + + Range.fromJSON = function (json) { + return new Range(json.start, json.end, json.step); + }; + + return Range; +} + +exports.name = 'Range'; +exports.path = 'type'; +exports.factory = factory; +},{"../../utils/number":54}],42:[function(require,module,exports){ +'use strict'; + +function factory(type, config, load, typed) { + /** + * Create a Matrix. The function creates a new `math.type.Matrix` object from + * an `Array`. A Matrix has utility functions to manipulate the data in the + * matrix, like getting the size and getting or setting values in the matrix. + * Supported storage formats are 'dense' and 'sparse'. + * + * Syntax: + * + * math.matrix() // creates an empty matrix using default storage format (dense). + * math.matrix(data) // creates a matrix with initial data using default storage format (dense). + * math.matrix('dense') // creates an empty matrix using the given storage format. + * math.matrix(data, 'dense') // creates a matrix with initial data using the given storage format. + * math.matrix(data, 'sparse') // creates a sparse matrix with initial data. + * math.matrix(data, 'sparse', 'number') // creates a sparse matrix with initial data, number data type. + * + * Examples: + * + * let m = math.matrix([[1, 2], [3, 4]]) + * m.size() // Array [2, 2] + * m.resize([3, 2], 5) + * m.valueOf() // Array [[1, 2], [3, 4], [5, 5]] + * m.get([1, 0]) // number 3 + * + * See also: + * + * bignumber, boolean, complex, index, number, string, unit, sparse + * + * @param {Array | Matrix} [data] A multi dimensional array + * @param {string} [format] The Matrix storage format + * + * @return {Matrix} The created matrix + */ + var matrix = typed('matrix', { + '': function _() { + return _create([]); + }, + 'string': function string(format) { + return _create([], format); + }, + 'string, string': function stringString(format, datatype) { + return _create([], format, datatype); + }, + 'Array': function Array(data) { + return _create(data); + }, + 'Matrix': function Matrix(data) { + return _create(data, data.storage()); + }, + 'Array | Matrix, string': _create, + 'Array | Matrix, string, string': _create + }); + matrix.toTex = { + 0: '\\begin{bmatrix}\\end{bmatrix}', + 1: "\\left(${args[0]}\\right)", + 2: "\\left(${args[0]}\\right)" + }; + return matrix; + /** + * Create a new Matrix with given storage format + * @param {Array} data + * @param {string} [format] + * @param {string} [datatype] + * @returns {Matrix} Returns a new Matrix + * @private + */ + + function _create(data, format, datatype) { + // get storage format constructor + var M = type.Matrix.storage(format || 'default'); // create instance + + return new M(data, datatype); + } +} + +exports.name = 'matrix'; +exports.factory = factory; +},{}],43:[function(require,module,exports){ +'use strict'; + +var deepMap = require('./../utils/collection/deepMap'); + +function factory(type, config, load, typed) { + /** + * Create a number or convert a string, boolean, or unit to a number. + * When value is a matrix, all elements will be converted to number. + * + * Syntax: + * + * math.number(value) + * math.number(unit, valuelessUnit) + * + * Examples: + * + * math.number(2) // returns number 2 + * math.number('7.2') // returns number 7.2 + * math.number(true) // returns number 1 + * math.number([true, false, true, true]) // returns [1, 0, 1, 1] + * math.number(math.unit('52cm'), 'm') // returns 0.52 + * + * See also: + * + * bignumber, boolean, complex, index, matrix, string, unit + * + * @param {string | number | BigNumber | Fraction | boolean | Array | Matrix | Unit | null} [value] Value to be converted + * @param {Unit | string} [valuelessUnit] A valueless unit, used to convert a unit to a number + * @return {number | Array | Matrix} The created number + */ + var number = typed('number', { + '': function _() { + return 0; + }, + 'number': function number(x) { + return x; + }, + 'string': function string(x) { + if (x === 'NaN') return NaN; + var num = Number(x); + + if (isNaN(num)) { + throw new SyntaxError('String "' + x + '" is no valid number'); + } + + return num; + }, + 'BigNumber': function BigNumber(x) { + return x.toNumber(); + }, + 'Fraction': function Fraction(x) { + return x.valueOf(); + }, + 'Unit': function Unit(x) { + throw new Error('Second argument with valueless unit expected'); + }, + 'null': function _null(x) { + return 0; + }, + 'Unit, string | Unit': function UnitStringUnit(unit, valuelessUnit) { + return unit.toNumber(valuelessUnit); + }, + 'Array | Matrix': function ArrayMatrix(x) { + return deepMap(x, number); + } + }); + number.toTex = { + 0: "0", + 1: "\\left(${args[0]}\\right)", + 2: "\\left(\\left(${args[0]}\\right)${args[1]}\\right)" + }; + return number; +} + +exports.name = 'number'; +exports.factory = factory; +},{"./../utils/collection/deepMap":49}],44:[function(require,module,exports){ +'use strict'; + +function factory(type, config, load, typed) { + var getTypeOf = load(require('../function/utils/typeof')); + var validInputTypes = { + 'string': true, + 'number': true, + 'BigNumber': true, + 'Fraction': true // Load the conversion functions for each output type + + }; + var validOutputTypes = { + 'number': load(require('./number')), + 'BigNumber': load(require('./bignumber/function/bignumber')), + 'Fraction': load(require('./fraction/function/fraction')) + /** + * Convert a numeric value to a specific type: number, BigNumber, or Fraction + * + * @param {string | number | BigNumber | Fraction } value + * @param {'number' | 'BigNumber' | 'Fraction'} outputType + * @return {number | BigNumber | Fraction} Returns an instance of the + * numeric in the requested type + */ + + }; + + var numeric = function numeric(value, outputType) { + var inputType = getTypeOf(value); + + if (!(inputType in validInputTypes)) { + throw new TypeError('Cannot convert ' + value + ' of type "' + inputType + '"; valid input types are ' + Object.keys(validInputTypes).join(', ')); + } + + if (!(outputType in validOutputTypes)) { + throw new TypeError('Cannot convert ' + value + ' to type "' + outputType + '"; valid output types are ' + Object.keys(validOutputTypes).join(', ')); + } + + if (outputType === inputType) { + return value; + } else { + return validOutputTypes[outputType](value); + } + }; + + numeric.toTex = function (node, options) { + // Not sure if this is strictly right but should work correctly for the vast majority of use cases. + return node.args[0].toTex(); + }; + + return numeric; +} // FIXME: expose numeric in the math namespace after we've decided on a name and have written proper docs for this function. See https://github.com/josdejong/mathjs/pull/1270 +// exports.name = 'type._numeric' + + +exports.path = 'type'; +exports.name = '_numeric'; +exports.factory = factory; +},{"../function/utils/typeof":38,"./bignumber/function/bignumber":39,"./fraction/function/fraction":40,"./number":43}],45:[function(require,module,exports){ +'use strict'; + +function factory(type, config, load, typed) { + /** + * A ResultSet contains a list or results + * @class ResultSet + * @param {Array} entries + * @constructor ResultSet + */ + function ResultSet(entries) { + if (!(this instanceof ResultSet)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this.entries = entries || []; + } + /** + * Attach type information + */ + + + ResultSet.prototype.type = 'ResultSet'; + ResultSet.prototype.isResultSet = true; + /** + * Returns the array with results hold by this ResultSet + * @memberof ResultSet + * @returns {Array} entries + */ + + ResultSet.prototype.valueOf = function () { + return this.entries; + }; + /** + * Returns the stringified results of the ResultSet + * @memberof ResultSet + * @returns {string} string + */ + + + ResultSet.prototype.toString = function () { + return '[' + this.entries.join(', ') + ']'; + }; + /** + * Get a JSON representation of the ResultSet + * @memberof ResultSet + * @returns {Object} Returns a JSON object structured as: + * `{"mathjs": "ResultSet", "entries": [...]}` + */ + + + ResultSet.prototype.toJSON = function () { + return { + mathjs: 'ResultSet', + entries: this.entries + }; + }; + /** + * Instantiate a ResultSet from a JSON object + * @memberof ResultSet + * @param {Object} json A JSON object structured as: + * `{"mathjs": "ResultSet", "entries": [...]}` + * @return {ResultSet} + */ + + + ResultSet.fromJSON = function (json) { + return new ResultSet(json.entries); + }; + + return ResultSet; +} + +exports.name = 'ResultSet'; +exports.path = 'type'; +exports.factory = factory; +},{}],46:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.size = size; +exports.validate = validate; +exports.validateIndex = validateIndex; +exports.resize = resize; +exports.reshape = reshape; +exports.squeeze = squeeze; +exports.unsqueeze = unsqueeze; +exports.flatten = flatten; +exports.map = map; +exports.forEach = forEach; +exports.filter = filter; +exports.filterRegExp = filterRegExp; +exports.join = join; +exports.identify = identify; +exports.generalize = generalize; + +var _number = _interopRequireDefault(require("./number")); + +var _string = _interopRequireDefault(require("./string")); + +var _DimensionError = _interopRequireDefault(require("../error/DimensionError")); + +var _IndexError = _interopRequireDefault(require("../error/IndexError")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Calculate the size of a multi dimensional array. + * This function checks the size of the first entry, it does not validate + * whether all dimensions match. (use function `validate` for that) + * @param {Array} x + * @Return {Number[]} size + */ +function size(x) { + var s = []; + + while (Array.isArray(x)) { + s.push(x.length); + x = x[0]; + } + + return s; +} +/** + * Recursively validate whether each element in a multi dimensional array + * has a size corresponding to the provided size array. + * @param {Array} array Array to be validated + * @param {number[]} size Array with the size of each dimension + * @param {number} dim Current dimension + * @throws DimensionError + * @private + */ + + +function _validate(array, size, dim) { + var i; + var len = array.length; + + if (len !== size[dim]) { + throw new _DimensionError.default(len, size[dim]); + } + + if (dim < size.length - 1) { + // recursively validate each child array + var dimNext = dim + 1; + + for (i = 0; i < len; i++) { + var child = array[i]; + + if (!Array.isArray(child)) { + throw new _DimensionError.default(size.length - 1, size.length, '<'); + } + + _validate(array[i], size, dimNext); + } + } else { + // last dimension. none of the childs may be an array + for (i = 0; i < len; i++) { + if (Array.isArray(array[i])) { + throw new _DimensionError.default(size.length + 1, size.length, '>'); + } + } + } +} +/** + * Validate whether each element in a multi dimensional array has + * a size corresponding to the provided size array. + * @param {Array} array Array to be validated + * @param {number[]} size Array with the size of each dimension + * @throws DimensionError + */ + + +function validate(array, size) { + var isScalar = size.length === 0; + + if (isScalar) { + // scalar + if (Array.isArray(array)) { + throw new _DimensionError.default(array.length, 0); + } + } else { + // array + _validate(array, size, 0); + } +} +/** + * Test whether index is an integer number with index >= 0 and index < length + * when length is provided + * @param {number} index Zero-based index + * @param {number} [length] Length of the array + */ + + +function validateIndex(index, length) { + if (!_number.default.isNumber(index) || !_number.default.isInteger(index)) { + throw new TypeError('Index must be an integer (value: ' + index + ')'); + } + + if (index < 0 || typeof length === 'number' && index >= length) { + throw new _IndexError.default(index, length); + } +} +/** + * Resize a multi dimensional array. The resized array is returned. + * @param {Array} array Array to be resized + * @param {Array.<number>} size Array with the size of each dimension + * @param {*} [defaultValue=0] Value to be filled in in new entries, + * zero by default. Specify for example `null`, + * to clearly see entries that are not explicitly + * set. + * @return {Array} array The resized array + */ + + +function resize(array, size, defaultValue) { + // TODO: add support for scalars, having size=[] ? + // check the type of the arguments + if (!Array.isArray(array) || !Array.isArray(size)) { + throw new TypeError('Array expected'); + } + + if (size.length === 0) { + throw new Error('Resizing to scalar is not supported'); + } // check whether size contains positive integers + + + size.forEach(function (value) { + if (!_number.default.isNumber(value) || !_number.default.isInteger(value) || value < 0) { + throw new TypeError('Invalid size, must contain positive integers ' + '(size: ' + _string.default.format(size) + ')'); + } + }); // recursively resize the array + + var _defaultValue = defaultValue !== undefined ? defaultValue : 0; + + _resize(array, size, 0, _defaultValue); + + return array; +} +/** + * Recursively resize a multi dimensional array + * @param {Array} array Array to be resized + * @param {number[]} size Array with the size of each dimension + * @param {number} dim Current dimension + * @param {*} [defaultValue] Value to be filled in in new entries, + * undefined by default. + * @private + */ + + +function _resize(array, size, dim, defaultValue) { + var i; + var elem; + var oldLen = array.length; + var newLen = size[dim]; + var minLen = Math.min(oldLen, newLen); // apply new length + + array.length = newLen; + + if (dim < size.length - 1) { + // non-last dimension + var dimNext = dim + 1; // resize existing child arrays + + for (i = 0; i < minLen; i++) { + // resize child array + elem = array[i]; + + if (!Array.isArray(elem)) { + elem = [elem]; // add a dimension + + array[i] = elem; + } + + _resize(elem, size, dimNext, defaultValue); + } // create new child arrays + + + for (i = minLen; i < newLen; i++) { + // get child array + elem = []; + array[i] = elem; // resize new child array + + _resize(elem, size, dimNext, defaultValue); + } + } else { + // last dimension + // remove dimensions of existing values + for (i = 0; i < minLen; i++) { + while (Array.isArray(array[i])) { + array[i] = array[i][0]; + } + } // fill new elements with the default value + + + for (i = minLen; i < newLen; i++) { + array[i] = defaultValue; + } + } +} +/** + * Re-shape a multi dimensional array to fit the specified dimensions + * @param {Array} array Array to be reshaped + * @param {Array.<number>} sizes List of sizes for each dimension + * @returns {Array} Array whose data has been formatted to fit the + * specified dimensions + * + * @throws {DimensionError} If the product of the new dimension sizes does + * not equal that of the old ones + */ + + +function reshape(array, sizes) { + var flatArray = flatten(array); + var newArray; + + function product(arr) { + return arr.reduce(function (prev, curr) { + return prev * curr; + }); + } + + if (!Array.isArray(array) || !Array.isArray(sizes)) { + throw new TypeError('Array expected'); + } + + if (sizes.length === 0) { + throw new _DimensionError.default(0, product(size(array)), '!='); + } + + var totalSize = 1; + + for (var sizeIndex = 0; sizeIndex < sizes.length; sizeIndex++) { + totalSize *= sizes[sizeIndex]; + } + + if (flatArray.length !== totalSize) { + throw new _DimensionError.default(product(sizes), product(size(array)), '!='); + } + + try { + newArray = _reshape(flatArray, sizes); + } catch (e) { + if (e instanceof _DimensionError.default) { + throw new _DimensionError.default(product(sizes), product(size(array)), '!='); + } + + throw e; + } + + return newArray; +} +/** + * Iteratively re-shape a multi dimensional array to fit the specified dimensions + * @param {Array} array Array to be reshaped + * @param {Array.<number>} sizes List of sizes for each dimension + * @returns {Array} Array whose data has been formatted to fit the + * specified dimensions + */ + + +function _reshape(array, sizes) { + // testing if there are enough elements for the requested shape + var tmpArray = array; + var tmpArray2; // for each dimensions starting by the last one and ignoring the first one + + for (var sizeIndex = sizes.length - 1; sizeIndex > 0; sizeIndex--) { + var size = sizes[sizeIndex]; + tmpArray2 = []; // aggregate the elements of the current tmpArray in elements of the requested size + + var length = tmpArray.length / size; + + for (var i = 0; i < length; i++) { + tmpArray2.push(tmpArray.slice(i * size, (i + 1) * size)); + } // set it as the new tmpArray for the next loop turn or for return + + + tmpArray = tmpArray2; + } + + return tmpArray; +} +/** + * Squeeze a multi dimensional array + * @param {Array} array + * @param {Array} [arraySize] + * @returns {Array} returns the array itself + */ + + +function squeeze(array, arraySize) { + var s = arraySize || size(array); // squeeze outer dimensions + + while (Array.isArray(array) && array.length === 1) { + array = array[0]; + s.shift(); + } // find the first dimension to be squeezed + + + var dims = s.length; + + while (s[dims - 1] === 1) { + dims--; + } // squeeze inner dimensions + + + if (dims < s.length) { + array = _squeeze(array, dims, 0); + s.length = dims; + } + + return array; +} +/** + * Recursively squeeze a multi dimensional array + * @param {Array} array + * @param {number} dims Required number of dimensions + * @param {number} dim Current dimension + * @returns {Array | *} Returns the squeezed array + * @private + */ + + +function _squeeze(array, dims, dim) { + var i, ii; + + if (dim < dims) { + var next = dim + 1; + + for (i = 0, ii = array.length; i < ii; i++) { + array[i] = _squeeze(array[i], dims, next); + } + } else { + while (Array.isArray(array)) { + array = array[0]; + } + } + + return array; +} +/** + * Unsqueeze a multi dimensional array: add dimensions when missing + * + * Paramter `size` will be mutated to match the new, unqueezed matrix size. + * + * @param {Array} array + * @param {number} dims Desired number of dimensions of the array + * @param {number} [outer] Number of outer dimensions to be added + * @param {Array} [arraySize] Current size of array. + * @returns {Array} returns the array itself + * @private + */ + + +function unsqueeze(array, dims, outer, arraySize) { + var s = arraySize || size(array); // unsqueeze outer dimensions + + if (outer) { + for (var i = 0; i < outer; i++) { + array = [array]; + s.unshift(1); + } + } // unsqueeze inner dimensions + + + array = _unsqueeze(array, dims, 0); + + while (s.length < dims) { + s.push(1); + } + + return array; +} +/** + * Recursively unsqueeze a multi dimensional array + * @param {Array} array + * @param {number} dims Required number of dimensions + * @param {number} dim Current dimension + * @returns {Array | *} Returns the squeezed array + * @private + */ + + +function _unsqueeze(array, dims, dim) { + var i, ii; + + if (Array.isArray(array)) { + var next = dim + 1; + + for (i = 0, ii = array.length; i < ii; i++) { + array[i] = _unsqueeze(array[i], dims, next); + } + } else { + for (var d = dim; d < dims; d++) { + array = [array]; + } + } + + return array; +} +/** + * Flatten a multi dimensional array, put all elements in a one dimensional + * array + * @param {Array} array A multi dimensional array + * @return {Array} The flattened array (1 dimensional) + */ + + +function flatten(array) { + if (!Array.isArray(array)) { + // if not an array, return as is + return array; + } + + var flat = []; + array.forEach(function callback(value) { + if (Array.isArray(value)) { + value.forEach(callback); // traverse through sub-arrays recursively + } else { + flat.push(value); + } + }); + return flat; +} +/** + * A safe map + * @param {Array} array + * @param {function} callback + */ + + +function map(array, callback) { + return Array.prototype.map.call(array, callback); +} +/** + * A safe forEach + * @param {Array} array + * @param {function} callback + */ + + +function forEach(array, callback) { + Array.prototype.forEach.call(array, callback); +} +/** + * A safe filter + * @param {Array} array + * @param {function} callback + */ + + +function filter(array, callback) { + if (size(array).length !== 1) { + throw new Error('Only one dimensional matrices supported'); + } + + return Array.prototype.filter.call(array, callback); +} +/** + * Filter values in a callback given a regular expression + * @param {Array} array + * @param {RegExp} regexp + * @return {Array} Returns the filtered array + * @private + */ + + +function filterRegExp(array, regexp) { + if (size(array).length !== 1) { + throw new Error('Only one dimensional matrices supported'); + } + + return Array.prototype.filter.call(array, function (entry) { + return regexp.test(entry); + }); +} +/** + * A safe join + * @param {Array} array + * @param {string} separator + */ + + +function join(array, separator) { + return Array.prototype.join.call(array, separator); +} +/** + * Assign a numeric identifier to every element of a sorted array + * @param {Array} a An array + * @return {Array} An array of objects containing the original value and its identifier + */ + + +function identify(a) { + if (!Array.isArray(a)) { + throw new TypeError('Array input expected'); + } + + if (a.length === 0) { + return a; + } + + var b = []; + var count = 0; + b[0] = { + value: a[0], + identifier: 0 + }; + + for (var i = 1; i < a.length; i++) { + if (a[i] === a[i - 1]) { + count++; + } else { + count = 0; + } + + b.push({ + value: a[i], + identifier: count + }); + } + + return b; +} +/** + * Remove the numeric identifier from the elements + * @param {array} a An array + * @return {array} An array of values without identifiers + */ + + +function generalize(a) { + if (!Array.isArray(a)) { + throw new TypeError('Array input expected'); + } + + if (a.length === 0) { + return a; + } + + var b = []; + + for (var i = 0; i < a.length; i++) { + b.push(a[i].value); + } + + return b; +} +},{"../error/DimensionError":9,"../error/IndexError":10,"./number":54,"./string":57}],47:[function(require,module,exports){ +'use strict'; + +var objectUtils = require('../object'); +/** + * Convert a BigNumber to a formatted string representation. + * + * Syntax: + * + * format(value) + * format(value, options) + * format(value, precision) + * format(value, fn) + * + * Where: + * + * {number} value The value to be formatted + * {Object} options An object with formatting options. Available options: + * {string} notation + * Number notation. Choose from: + * 'fixed' Always use regular number notation. + * For example '123.40' and '14000000' + * 'exponential' Always use exponential notation. + * For example '1.234e+2' and '1.4e+7' + * 'auto' (default) Regular number notation for numbers + * having an absolute value between + * `lower` and `upper` bounds, and uses + * exponential notation elsewhere. + * Lower bound is included, upper bound + * is excluded. + * For example '123.4' and '1.4e7'. + * {number} precision A number between 0 and 16 to round + * the digits of the number. + * In case of notations 'exponential', + * 'engineering', and 'auto', + * `precision` defines the total + * number of significant digits returned. + * In case of notation 'fixed', + * `precision` defines the number of + * significant digits after the decimal + * point. + * `precision` is undefined by default. + * {number} lowerExp Exponent determining the lower boundary + * for formatting a value with an exponent + * when `notation='auto`. + * Default value is `-3`. + * {number} upperExp Exponent determining the upper boundary + * for formatting a value with an exponent + * when `notation='auto`. + * Default value is `5`. + * {Function} fn A custom formatting function. Can be used to override the + * built-in notations. Function `fn` is called with `value` as + * parameter and must return a string. Is useful for example to + * format all values inside a matrix in a particular way. + * + * Examples: + * + * format(6.4) // '6.4' + * format(1240000) // '1.24e6' + * format(1/3) // '0.3333333333333333' + * format(1/3, 3) // '0.333' + * format(21385, 2) // '21000' + * format(12e8, {notation: 'fixed'}) // returns '1200000000' + * format(2.3, {notation: 'fixed', precision: 4}) // returns '2.3000' + * format(52.8, {notation: 'exponential'}) // returns '5.28e+1' + * format(12400, {notation: 'engineering'}) // returns '12.400e+3' + * + * @param {BigNumber} value + * @param {Object | Function | number} [options] + * @return {string} str The formatted value + */ + + +exports.format = function (value, options) { + if (typeof options === 'function') { + // handle format(value, fn) + return options(value); + } // handle special cases + + + if (!value.isFinite()) { + return value.isNaN() ? 'NaN' : value.gt(0) ? 'Infinity' : '-Infinity'; + } // default values for options + + + var notation = 'auto'; + var precision; + + if (options !== undefined) { + // determine notation from options + if (options.notation) { + notation = options.notation; + } // determine precision from options + + + if (typeof options === 'number') { + precision = options; + } else if (options.precision) { + precision = options.precision; + } + } // handle the various notations + + + switch (notation) { + case 'fixed': + return exports.toFixed(value, precision); + + case 'exponential': + return exports.toExponential(value, precision); + + case 'auto': + // TODO: clean up some day. Deprecated since: 2018-01-24 + // @deprecated upper and lower are replaced with upperExp and lowerExp since v4.0.0 + if (options && options.exponential && (options.exponential.lower !== undefined || options.exponential.upper !== undefined)) { + var fixedOptions = objectUtils.map(options, function (x) { + return x; + }); + fixedOptions.exponential = undefined; + + if (options.exponential.lower !== undefined) { + fixedOptions.lowerExp = Math.round(Math.log(options.exponential.lower) / Math.LN10); + } + + if (options.exponential.upper !== undefined) { + fixedOptions.upperExp = Math.round(Math.log(options.exponential.upper) / Math.LN10); + } + + console.warn('Deprecation warning: Formatting options exponential.lower and exponential.upper ' + '(minimum and maximum value) ' + 'are replaced with exponential.lowerExp and exponential.upperExp ' + '(minimum and maximum exponent) since version 4.0.0. ' + 'Replace ' + JSON.stringify(options) + ' with ' + JSON.stringify(fixedOptions)); + return exports.format(value, fixedOptions); + } // determine lower and upper bound for exponential notation. + // TODO: implement support for upper and lower to be BigNumbers themselves + + + var lowerExp = options && options.lowerExp !== undefined ? options.lowerExp : -3; + var upperExp = options && options.upperExp !== undefined ? options.upperExp : 5; // handle special case zero + + if (value.isZero()) return '0'; // determine whether or not to output exponential notation + + var str; + var exp = value.logarithm(); + + if (exp.gte(lowerExp) && exp.lt(upperExp)) { + // normal number notation + str = value.toSignificantDigits(precision).toFixed(); + } else { + // exponential notation + str = exports.toExponential(value, precision); + } // remove trailing zeros after the decimal point + + + return str.replace(/((\.\d*?)(0+))($|e)/, function () { + var digits = arguments[2]; + var e = arguments[4]; + return digits !== '.' ? digits + e : e; + }); + + default: + throw new Error('Unknown notation "' + notation + '". ' + 'Choose "auto", "exponential", or "fixed".'); + } +}; +/** + * Format a number in exponential notation. Like '1.23e+5', '2.3e+0', '3.500e-3' + * @param {BigNumber} value + * @param {number} [precision] Number of digits in formatted output. + * If not provided, the maximum available digits + * is used. + * @returns {string} str + */ + + +exports.toExponential = function (value, precision) { + if (precision !== undefined) { + return value.toExponential(precision - 1); // Note the offset of one + } else { + return value.toExponential(); + } +}; +/** + * Format a number with fixed notation. + * @param {BigNumber} value + * @param {number} [precision=undefined] Optional number of decimals after the + * decimal point. Undefined by default. + */ + + +exports.toFixed = function (value, precision) { + return value.toFixed(precision); +}; +},{"../object":55}],48:[function(require,module,exports){ +'use strict'; +/** + * Test whether a value is a BigNumber + * @param {*} x + * @return {boolean} + */ + +module.exports = function isBigNumber(x) { + return x && x.constructor.prototype.isBigNumber || false; +}; +},{}],49:[function(require,module,exports){ +'use strict'; +/** + * Execute the callback function element wise for each element in array and any + * nested array + * Returns an array with the results + * @param {Array | Matrix} array + * @param {Function} callback The callback is called with two parameters: + * value1 and value2, which contain the current + * element of both arrays. + * @param {boolean} [skipZeros] Invoke callback function for non-zero values only. + * + * @return {Array | Matrix} res + */ + +module.exports = function deepMap(array, callback, skipZeros) { + if (array && typeof array.map === 'function') { + // TODO: replace array.map with a for loop to improve performance + return array.map(function (x) { + return deepMap(x, callback, skipZeros); + }); + } else { + return callback(array); + } +}; +},{}],50:[function(require,module,exports){ +'use strict'; +/** + * Test whether a value is a Matrix + * @param {*} x + * @returns {boolean} returns true with input is a Matrix + * (like a DenseMatrix or SparseMatrix) + */ + +module.exports = function isMatrix(x) { + return x && x.constructor.prototype.isMatrix || false; +}; +},{}],51:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var hasOwnProperty = require('./object').hasOwnProperty; +/** + * Get a property of a plain object + * Throws an error in case the object is not a plain object or the + * property is not defined on the object itself + * @param {Object} object + * @param {string} prop + * @return {*} Returns the property value when safe + */ + + +function getSafeProperty(object, prop) { + // only allow getting safe properties of a plain object + if (isPlainObject(object) && isSafeProperty(object, prop)) { + return object[prop]; + } + + if (typeof object[prop] === 'function' && isSafeMethod(object, prop)) { + throw new Error('Cannot access method "' + prop + '" as a property'); + } + + throw new Error('No access to property "' + prop + '"'); +} +/** + * Set a property on a plain object. + * Throws an error in case the object is not a plain object or the + * property would override an inherited property like .constructor or .toString + * @param {Object} object + * @param {string} prop + * @param {*} value + * @return {*} Returns the value + */ +// TODO: merge this function into access.js? + + +function setSafeProperty(object, prop, value) { + // only allow setting safe properties of a plain object + if (isPlainObject(object) && isSafeProperty(object, prop)) { + object[prop] = value; + return value; + } + + throw new Error('No access to property "' + prop + '"'); +} +/** + * Test whether a property is safe to use for an object. + * For example .toString and .constructor are not safe + * @param {string} prop + * @return {boolean} Returns true when safe + */ + + +function isSafeProperty(object, prop) { + if (!object || _typeof(object) !== 'object') { + return false; + } // SAFE: whitelisted + // e.g length + + + if (hasOwnProperty(safeNativeProperties, prop)) { + return true; + } // UNSAFE: inherited from Object prototype + // e.g constructor + + + if (prop in Object.prototype) { + // 'in' is used instead of hasOwnProperty for nodejs v0.10 + // which is inconsistent on root prototypes. It is safe + // here because Object.prototype is a root object + return false; + } // UNSAFE: inherited from Function prototype + // e.g call, apply + + + if (prop in Function.prototype) { + // 'in' is used instead of hasOwnProperty for nodejs v0.10 + // which is inconsistent on root prototypes. It is safe + // here because Function.prototype is a root object + return false; + } + + return true; +} +/** + * Validate whether a method is safe. + * Throws an error when that's not the case. + * @param {Object} object + * @param {string} method + */ +// TODO: merge this function into assign.js? + + +function validateSafeMethod(object, method) { + if (!isSafeMethod(object, method)) { + throw new Error('No access to method "' + method + '"'); + } +} +/** + * Check whether a method is safe. + * Throws an error when that's not the case (for example for `constructor`). + * @param {Object} object + * @param {string} method + * @return {boolean} Returns true when safe, false otherwise + */ + + +function isSafeMethod(object, method) { + if (!object || typeof object[method] !== 'function') { + return false; + } // UNSAFE: ghosted + // e.g overridden toString + // Note that IE10 doesn't support __proto__ and we can't do this check there. + + + if (hasOwnProperty(object, method) && Object.getPrototypeOf && method in Object.getPrototypeOf(object)) { + return false; + } // SAFE: whitelisted + // e.g toString + + + if (hasOwnProperty(safeNativeMethods, method)) { + return true; + } // UNSAFE: inherited from Object prototype + // e.g constructor + + + if (method in Object.prototype) { + // 'in' is used instead of hasOwnProperty for nodejs v0.10 + // which is inconsistent on root prototypes. It is safe + // here because Object.prototype is a root object + return false; + } // UNSAFE: inherited from Function prototype + // e.g call, apply + + + if (method in Function.prototype) { + // 'in' is used instead of hasOwnProperty for nodejs v0.10 + // which is inconsistent on root prototypes. It is safe + // here because Function.prototype is a root object + return false; + } + + return true; +} + +function isPlainObject(object) { + return _typeof(object) === 'object' && object && object.constructor === Object; +} + +var safeNativeProperties = { + length: true, + name: true +}; +var safeNativeMethods = { + toString: true, + valueOf: true, + toLocaleString: true +}; +exports.getSafeProperty = getSafeProperty; +exports.setSafeProperty = setSafeProperty; +exports.isSafeProperty = isSafeProperty; +exports.validateSafeMethod = validateSafeMethod; +exports.isSafeMethod = isSafeMethod; +exports.isPlainObject = isPlainObject; +},{"./object":55}],52:[function(require,module,exports){ +'use strict'; + +var Emitter = require('tiny-emitter'); +/** + * Extend given object with emitter functions `on`, `off`, `once`, `emit` + * @param {Object} obj + * @return {Object} obj + */ + + +exports.mixin = function (obj) { + // create event emitter + var emitter = new Emitter(); // bind methods to obj (we don't want to expose the emitter.e Array...) + + obj.on = emitter.on.bind(emitter); + obj.off = emitter.off.bind(emitter); + obj.once = emitter.once.bind(emitter); + obj.emit = emitter.emit.bind(emitter); + return obj; +}; +},{"tiny-emitter":58}],53:[function(require,module,exports){ +'use strict'; + +var escapeLatex = require('escape-latex'); + +exports.symbols = { + // GREEK LETTERS + Alpha: 'A', + alpha: '\\alpha', + Beta: 'B', + beta: '\\beta', + Gamma: '\\Gamma', + gamma: '\\gamma', + Delta: '\\Delta', + delta: '\\delta', + Epsilon: 'E', + epsilon: '\\epsilon', + varepsilon: '\\varepsilon', + Zeta: 'Z', + zeta: '\\zeta', + Eta: 'H', + eta: '\\eta', + Theta: '\\Theta', + theta: '\\theta', + vartheta: '\\vartheta', + Iota: 'I', + iota: '\\iota', + Kappa: 'K', + kappa: '\\kappa', + varkappa: '\\varkappa', + Lambda: '\\Lambda', + lambda: '\\lambda', + Mu: 'M', + mu: '\\mu', + Nu: 'N', + nu: '\\nu', + Xi: '\\Xi', + xi: '\\xi', + Omicron: 'O', + omicron: 'o', + Pi: '\\Pi', + pi: '\\pi', + varpi: '\\varpi', + Rho: 'P', + rho: '\\rho', + varrho: '\\varrho', + Sigma: '\\Sigma', + sigma: '\\sigma', + varsigma: '\\varsigma', + Tau: 'T', + tau: '\\tau', + Upsilon: "\\Upsilon", + upsilon: "\\upsilon", + Phi: '\\Phi', + phi: '\\phi', + varphi: '\\varphi', + Chi: 'X', + chi: '\\chi', + Psi: '\\Psi', + psi: '\\psi', + Omega: '\\Omega', + omega: '\\omega', + // logic + 'true': '\\mathrm{True}', + 'false': '\\mathrm{False}', + // other + i: 'i', + // TODO use \i ?? + inf: '\\infty', + Inf: '\\infty', + infinity: '\\infty', + Infinity: '\\infty', + oo: '\\infty', + lim: '\\lim', + 'undefined': '\\mathbf{?}' +}; +exports.operators = { + 'transpose': '^\\top', + 'ctranspose': '^H', + 'factorial': '!', + 'pow': '^', + 'dotPow': '.^\\wedge', + // TODO find ideal solution + 'unaryPlus': '+', + 'unaryMinus': '-', + 'bitNot': '\\~', + // TODO find ideal solution + 'not': '\\neg', + 'multiply': '\\cdot', + 'divide': '\\frac', + // TODO how to handle that properly? + 'dotMultiply': '.\\cdot', + // TODO find ideal solution + 'dotDivide': '.:', + // TODO find ideal solution + 'mod': '\\mod', + 'add': '+', + 'subtract': '-', + 'to': '\\rightarrow', + 'leftShift': '<<', + 'rightArithShift': '>>', + 'rightLogShift': '>>>', + 'equal': '=', + 'unequal': '\\neq', + 'smaller': '<', + 'larger': '>', + 'smallerEq': '\\leq', + 'largerEq': '\\geq', + 'bitAnd': '\\&', + 'bitXor': "\\underline{|}", + 'bitOr': '|', + 'and': '\\wedge', + 'xor': '\\veebar', + 'or': '\\vee' +}; +exports.defaultTemplate = "\\mathrm{${name}}\\left(${args}\\right)"; +var units = { + deg: '^\\circ' +}; + +exports.escape = function (string) { + return escapeLatex(string, { + 'preserveFormatting': true + }); +}; // @param {string} name +// @param {boolean} isUnit + + +exports.toSymbol = function (name, isUnit) { + isUnit = typeof isUnit === 'undefined' ? false : isUnit; + + if (isUnit) { + if (units.hasOwnProperty(name)) { + return units[name]; + } + + return '\\mathrm{' + exports.escape(name) + '}'; + } + + if (exports.symbols.hasOwnProperty(name)) { + return exports.symbols[name]; + } + + return exports.escape(name); +}; +},{"escape-latex":2}],54:[function(require,module,exports){ +'use strict'; + +var objectUtils = require('./object'); +/** + * @typedef {{sign: '+' | '-' | '', coefficients: number[], exponent: number}} SplitValue + */ + +/** + * Test whether value is a number + * @param {*} value + * @return {boolean} isNumber + */ + + +exports.isNumber = function (value) { + return typeof value === 'number'; +}; +/** + * Check if a number is integer + * @param {number | boolean} value + * @return {boolean} isInteger + */ + + +exports.isInteger = function (value) { + if (typeof value === 'boolean') { + return true; + } + + return isFinite(value) ? value === Math.round(value) : false; // Note: we use ==, not ===, as we can have Booleans as well +}; +/** + * Calculate the sign of a number + * @param {number} x + * @returns {*} + */ + + +exports.sign = Math.sign || function (x) { + if (x > 0) { + return 1; + } else if (x < 0) { + return -1; + } else { + return 0; + } +}; +/** + * Convert a number to a formatted string representation. + * + * Syntax: + * + * format(value) + * format(value, options) + * format(value, precision) + * format(value, fn) + * + * Where: + * + * {number} value The value to be formatted + * {Object} options An object with formatting options. Available options: + * {string} notation + * Number notation. Choose from: + * 'fixed' Always use regular number notation. + * For example '123.40' and '14000000' + * 'exponential' Always use exponential notation. + * For example '1.234e+2' and '1.4e+7' + * 'engineering' Always use engineering notation. + * For example '123.4e+0' and '14.0e+6' + * 'auto' (default) Regular number notation for numbers + * having an absolute value between + * `lowerExp` and `upperExp` bounds, and + * uses exponential notation elsewhere. + * Lower bound is included, upper bound + * is excluded. + * For example '123.4' and '1.4e7'. + * {number} precision A number between 0 and 16 to round + * the digits of the number. + * In case of notations 'exponential', + * 'engineering', and 'auto', + * `precision` defines the total + * number of significant digits returned. + * In case of notation 'fixed', + * `precision` defines the number of + * significant digits after the decimal + * point. + * `precision` is undefined by default, + * not rounding any digits. + * {number} lowerExp Exponent determining the lower boundary + * for formatting a value with an exponent + * when `notation='auto`. + * Default value is `-3`. + * {number} upperExp Exponent determining the upper boundary + * for formatting a value with an exponent + * when `notation='auto`. + * Default value is `5`. + * {Function} fn A custom formatting function. Can be used to override the + * built-in notations. Function `fn` is called with `value` as + * parameter and must return a string. Is useful for example to + * format all values inside a matrix in a particular way. + * + * Examples: + * + * format(6.4) // '6.4' + * format(1240000) // '1.24e6' + * format(1/3) // '0.3333333333333333' + * format(1/3, 3) // '0.333' + * format(21385, 2) // '21000' + * format(12.071, {notation: 'fixed'}) // '12' + * format(2.3, {notation: 'fixed', precision: 2}) // '2.30' + * format(52.8, {notation: 'exponential'}) // '5.28e+1' + * format(12345678, {notation: 'engineering'}) // '12.345678e+6' + * + * @param {number} value + * @param {Object | Function | number} [options] + * @return {string} str The formatted value + */ + + +exports.format = function (value, options) { + if (typeof options === 'function') { + // handle format(value, fn) + return options(value); + } // handle special cases + + + if (value === Infinity) { + return 'Infinity'; + } else if (value === -Infinity) { + return '-Infinity'; + } else if (isNaN(value)) { + return 'NaN'; + } // default values for options + + + var notation = 'auto'; + var precision; + + if (options) { + // determine notation from options + if (options.notation) { + notation = options.notation; + } // determine precision from options + + + if (exports.isNumber(options)) { + precision = options; + } else if (options.precision) { + precision = options.precision; + } + } // handle the various notations + + + switch (notation) { + case 'fixed': + return exports.toFixed(value, precision); + + case 'exponential': + return exports.toExponential(value, precision); + + case 'engineering': + return exports.toEngineering(value, precision); + + case 'auto': + // TODO: clean up some day. Deprecated since: 2018-01-24 + // @deprecated upper and lower are replaced with upperExp and lowerExp since v4.0.0 + if (options && options.exponential && (options.exponential.lower !== undefined || options.exponential.upper !== undefined)) { + var fixedOptions = objectUtils.map(options, function (x) { + return x; + }); + fixedOptions.exponential = undefined; + + if (options.exponential.lower !== undefined) { + fixedOptions.lowerExp = Math.round(Math.log(options.exponential.lower) / Math.LN10); + } + + if (options.exponential.upper !== undefined) { + fixedOptions.upperExp = Math.round(Math.log(options.exponential.upper) / Math.LN10); + } + + console.warn('Deprecation warning: Formatting options exponential.lower and exponential.upper ' + '(minimum and maximum value) ' + 'are replaced with exponential.lowerExp and exponential.upperExp ' + '(minimum and maximum exponent) since version 4.0.0. ' + 'Replace ' + JSON.stringify(options) + ' with ' + JSON.stringify(fixedOptions)); + return exports.toPrecision(value, precision, fixedOptions); + } + + return exports.toPrecision(value, precision, options && options) // remove trailing zeros after the decimal point + .replace(/((\.\d*?)(0+))($|e)/, function () { + var digits = arguments[2]; + var e = arguments[4]; + return digits !== '.' ? digits + e : e; + }); + + default: + throw new Error('Unknown notation "' + notation + '". ' + 'Choose "auto", "exponential", or "fixed".'); + } +}; +/** + * Split a number into sign, coefficients, and exponent + * @param {number | string} value + * @return {SplitValue} + * Returns an object containing sign, coefficients, and exponent + */ + + +exports.splitNumber = function (value) { + // parse the input value + var match = String(value).toLowerCase().match(/^0*?(-?)(\d+\.?\d*)(e([+-]?\d+))?$/); + + if (!match) { + throw new SyntaxError('Invalid number ' + value); + } + + var sign = match[1]; + var digits = match[2]; + var exponent = parseFloat(match[4] || '0'); + var dot = digits.indexOf('.'); + exponent += dot !== -1 ? dot - 1 : digits.length - 1; + var coefficients = digits.replace('.', '') // remove the dot (must be removed before removing leading zeros) + .replace(/^0*/, function (zeros) { + // remove leading zeros, add their count to the exponent + exponent -= zeros.length; + return ''; + }).replace(/0*$/, '') // remove trailing zeros + .split('').map(function (d) { + return parseInt(d); + }); + + if (coefficients.length === 0) { + coefficients.push(0); + exponent++; + } + + return { + sign: sign, + coefficients: coefficients, + exponent: exponent + }; +}; +/** + * Format a number in engineering notation. Like '1.23e+6', '2.3e+0', '3.500e-3' + * @param {number | string} value + * @param {number} [precision] Optional number of significant figures to return. + */ + + +exports.toEngineering = function (value, precision) { + if (isNaN(value) || !isFinite(value)) { + return String(value); + } + + var rounded = exports.roundDigits(exports.splitNumber(value), precision); + var e = rounded.exponent; + var c = rounded.coefficients; // find nearest lower multiple of 3 for exponent + + var newExp = e % 3 === 0 ? e : e < 0 ? e - 3 - e % 3 : e - e % 3; + + if (exports.isNumber(precision)) { + // add zeroes to give correct sig figs + if (precision > c.length) c = c.concat(zeros(precision - c.length)); + } else { + // concatenate coefficients with necessary zeros + var significandsDiff = e >= 0 ? e : Math.abs(newExp); // add zeros if necessary (for ex: 1e+8) + + if (c.length - 1 < significandsDiff) c = c.concat(zeros(significandsDiff - (c.length - 1))); + } // find difference in exponents + + + var expDiff = Math.abs(e - newExp); + var decimalIdx = 1; // push decimal index over by expDiff times + + while (--expDiff >= 0) { + decimalIdx++; + } // if all coefficient values are zero after the decimal point and precision is unset, don't add a decimal value. + // otherwise concat with the rest of the coefficients + + + var decimals = c.slice(decimalIdx).join(''); + var decimalVal = exports.isNumber(precision) && decimals.length || decimals.match(/[1-9]/) ? '.' + decimals : ''; + var str = c.slice(0, decimalIdx).join('') + decimalVal + 'e' + (e >= 0 ? '+' : '') + newExp.toString(); + return rounded.sign + str; +}; +/** + * Format a number with fixed notation. + * @param {number | string} value + * @param {number} [precision=undefined] Optional number of decimals after the + * decimal point. null by default. + */ + + +exports.toFixed = function (value, precision) { + if (isNaN(value) || !isFinite(value)) { + return String(value); + } + + var splitValue = exports.splitNumber(value); + var rounded = typeof precision === 'number' ? exports.roundDigits(splitValue, splitValue.exponent + 1 + precision) : splitValue; + var c = rounded.coefficients; + var p = rounded.exponent + 1; // exponent may have changed + // append zeros if needed + + var pp = p + (precision || 0); + + if (c.length < pp) { + c = c.concat(zeros(pp - c.length)); + } // prepend zeros if needed + + + if (p < 0) { + c = zeros(-p + 1).concat(c); + p = 1; + } // insert a dot if needed + + + if (p < c.length) { + c.splice(p, 0, p === 0 ? '0.' : '.'); + } + + return rounded.sign + c.join(''); +}; +/** + * Format a number in exponential notation. Like '1.23e+5', '2.3e+0', '3.500e-3' + * @param {number | string} value + * @param {number} [precision] Number of digits in formatted output. + * If not provided, the maximum available digits + * is used. + */ + + +exports.toExponential = function (value, precision) { + if (isNaN(value) || !isFinite(value)) { + return String(value); + } // round if needed, else create a clone + + + var split = exports.splitNumber(value); + var rounded = precision ? exports.roundDigits(split, precision) : split; + var c = rounded.coefficients; + var e = rounded.exponent; // append zeros if needed + + if (c.length < precision) { + c = c.concat(zeros(precision - c.length)); + } // format as `C.CCCe+EEE` or `C.CCCe-EEE` + + + var first = c.shift(); + return rounded.sign + first + (c.length > 0 ? '.' + c.join('') : '') + 'e' + (e >= 0 ? '+' : '') + e; +}; +/** + * Format a number with a certain precision + * @param {number | string} value + * @param {number} [precision=undefined] Optional number of digits. + * @param {{lowerExp: number | undefined, upperExp: number | undefined}} [options] + * By default: + * lowerExp = -3 (incl) + * upper = +5 (excl) + * @return {string} + */ + + +exports.toPrecision = function (value, precision, options) { + if (isNaN(value) || !isFinite(value)) { + return String(value); + } // determine lower and upper bound for exponential notation. + + + var lowerExp = options && options.lowerExp !== undefined ? options.lowerExp : -3; + var upperExp = options && options.upperExp !== undefined ? options.upperExp : 5; + var split = exports.splitNumber(value); + + if (split.exponent < lowerExp || split.exponent >= upperExp) { + // exponential notation + return exports.toExponential(value, precision); + } else { + var rounded = precision ? exports.roundDigits(split, precision) : split; + var c = rounded.coefficients; + var e = rounded.exponent; // append trailing zeros + + if (c.length < precision) { + c = c.concat(zeros(precision - c.length)); + } // append trailing zeros + // TODO: simplify the next statement + + + c = c.concat(zeros(e - c.length + 1 + (c.length < precision ? precision - c.length : 0))); // prepend zeros + + c = zeros(-e).concat(c); + var dot = e > 0 ? e : 0; + + if (dot < c.length - 1) { + c.splice(dot + 1, 0, '.'); + } + + return rounded.sign + c.join(''); + } +}; +/** + * Round the number of digits of a number * + * @param {SplitValue} split A value split with .splitNumber(value) + * @param {number} precision A positive integer + * @return {SplitValue} + * Returns an object containing sign, coefficients, and exponent + * with rounded digits + */ + + +exports.roundDigits = function (split, precision) { + // create a clone + var rounded = { + sign: split.sign, + coefficients: split.coefficients, + exponent: split.exponent + }; + var c = rounded.coefficients; // prepend zeros if needed + + while (precision <= 0) { + c.unshift(0); + rounded.exponent++; + precision++; + } + + if (c.length > precision) { + var removed = c.splice(precision, c.length - precision); + + if (removed[0] >= 5) { + var i = precision - 1; + c[i]++; + + while (c[i] === 10) { + c.pop(); + + if (i === 0) { + c.unshift(0); + rounded.exponent++; + i++; + } + + i--; + c[i]++; + } + } + } + + return rounded; +}; +/** + * Create an array filled with zeros. + * @param {number} length + * @return {Array} + */ + + +function zeros(length) { + var arr = []; + + for (var i = 0; i < length; i++) { + arr.push(0); + } + + return arr; +} +/** + * Count the number of significant digits of a number. + * + * For example: + * 2.34 returns 3 + * 0.0034 returns 2 + * 120.5e+30 returns 4 + * + * @param {number} value + * @return {number} digits Number of significant digits + */ + + +exports.digits = function (value) { + return value.toExponential().replace(/e.*$/, '') // remove exponential notation + .replace(/^0\.?0*|\./, '') // remove decimal point and leading zeros + .length; +}; +/** + * Minimum number added to one that makes the result different than one + */ + + +exports.DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16; +/** + * Compares two floating point numbers. + * @param {number} x First value to compare + * @param {number} y Second value to compare + * @param {number} [epsilon] The maximum relative difference between x and y + * If epsilon is undefined or null, the function will + * test whether x and y are exactly equal. + * @return {boolean} whether the two numbers are nearly equal +*/ + +exports.nearlyEqual = function (x, y, epsilon) { + // if epsilon is null or undefined, test whether x and y are exactly equal + if (epsilon === null || epsilon === undefined) { + return x === y; + } + + if (x === y) { + return true; + } // NaN + + + if (isNaN(x) || isNaN(y)) { + return false; + } // at this point x and y should be finite + + + if (isFinite(x) && isFinite(y)) { + // check numbers are very close, needed when comparing numbers near zero + var diff = Math.abs(x - y); + + if (diff < exports.DBL_EPSILON) { + return true; + } else { + // use relative error + return diff <= Math.max(Math.abs(x), Math.abs(y)) * epsilon; + } + } // Infinite and Number or negative Infinite and positive Infinite cases + + + return false; +}; +},{"./object":55}],55:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var isBigNumber = require('./bignumber/isBigNumber'); +/** + * 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 Date) return new Date(x.valueOf()); + if (isBigNumber(x)) return x; // bignumbers are immutable + + if (x instanceof RegExp) throw new TypeError('Cannot clone ' + x); // TODO: clone a RegExp + // object + + return exports.map(x, clone); +}; +/** + * Apply map to all properties of an object + * @param {Object} object + * @param {function} callback + * @return {Object} Returns a copy of the object with mapped properties + */ + + +exports.map = function (object, callback) { + var clone = {}; + + for (var key in object) { + if (exports.hasOwnProperty(object, key)) { + clone[key] = callback(object[key]); + } + } + + return clone; +}; +/** + * Extend object a with the properties of object b + * @param {Object} a + * @param {Object} b + * @return {Object} a + */ + + +exports.extend = function (a, b) { + for (var prop in b) { + if (exports.hasOwnProperty(b, 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 (exports.hasOwnProperty(b, 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 a === b; + } +}; +/** + * Test whether the current JavaScript engine supports Object.defineProperty + * @returns {boolean} returns true if supported + */ + + +exports.canDefineProperty = function () { + // test needed for broken IE8 implementation + try { + if (Object.defineProperty) { + Object.defineProperty({}, 'x', { + get: function get() {} + }); + return true; + } + } catch (e) {} + + return false; +}; +/** + * Attach a lazy loading property to a constant. + * The given function `fn` is called once when the property is first requested. + * On older browsers (<IE8), the function will fall back to direct evaluation + * of the properties value. + * @param {Object} object Object where to add the property + * @param {string} prop Property name + * @param {Function} fn Function returning the property value. Called + * without arguments. + */ + + +exports.lazy = function (object, prop, fn) { + if (exports.canDefineProperty()) { + var _uninitialized = true; + + var _value; + + Object.defineProperty(object, prop, { + get: function get() { + if (_uninitialized) { + _value = fn(); + _uninitialized = false; + } + + return _value; + }, + set: function set(value) { + _value = value; + _uninitialized = false; + }, + configurable: true, + enumerable: true + }); + } else { + // fall back to immediate evaluation + object[prop] = fn(); + } +}; +/** + * Traverse a path into an object. + * When a namespace is missing, it will be created + * @param {Object} object + * @param {string} path A dot separated string like 'name.space' + * @return {Object} Returns the object at the end of the path + */ + + +exports.traverse = function (object, path) { + var obj = object; + + if (path) { + var names = path.split('.'); + + for (var i = 0; i < names.length; i++) { + var name = names[i]; + + if (!(name in obj)) { + obj[name] = {}; + } + + obj = obj[name]; + } + } + + return obj; +}; +/** + * A safe hasOwnProperty + * @param {Object} object + * @param {string} property + */ + + +exports.hasOwnProperty = function (object, property) { + return object && Object.hasOwnProperty.call(object, property); +}; +/** + * Test whether an object is a factory. a factory has fields: + * + * - factory: function (type: Object, config: Object, load: function, typed: function [, math: Object]) (required) + * - name: string (optional) + * - path: string A dot separated path (optional) + * - math: boolean If true (false by default), the math namespace is passed + * as fifth argument of the factory function + * + * @param {*} object + * @returns {boolean} + */ + + +exports.isFactory = function (object) { + return object && typeof object.factory === 'function'; +}; +},{"./bignumber/isBigNumber":48}],56:[function(require,module,exports){ +"use strict"; + +// TODO: remove these polyfills as soon as we have a build process that transpiles the code to ES5 +// Polyfill for IE 11 (Number.isFinite is used in `complex.js`) +// source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite +Number.isFinite = Number.isFinite || function (value) { + return typeof value === 'number' && isFinite(value); +}; // Polyfill for IE 11 +// source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN + + +Number.isNaN = Number.isNaN || function (value) { + return value !== value; // eslint-disable-line no-self-compare +}; +},{}],57:[function(require,module,exports){ +'use strict'; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var formatNumber = require('./number').format; + +var formatBigNumber = require('./bignumber/formatter').format; + +var isBigNumber = require('./bignumber/isBigNumber'); +/** + * Test whether value is a string + * @param {*} value + * @return {boolean} isString + */ + + +exports.isString = function (value) { + return typeof value === 'string'; +}; +/** + * Check if a text ends with a certain string. + * @param {string} text + * @param {string} search + */ + + +exports.endsWith = function (text, search) { + var start = text.length - search.length; + var end = text.length; + return text.substring(start, end) === search; +}; +/** + * Format a value of any type into a string. + * + * Usage: + * math.format(value) + * math.format(value, precision) + * + * When value is a function: + * + * - When the function has a property `syntax`, it returns this + * syntax description. + * - In other cases, a string `'function'` is returned. + * + * When `value` is an Object: + * + * - When the object contains a property `format` being a function, this + * function is invoked as `value.format(options)` and the result is returned. + * - When the object has its own `toString` method, this method is invoked + * and the result is returned. + * - In other cases the function will loop over all object properties and + * return JSON object notation like '{"a": 2, "b": 3}'. + * + * Example usage: + * math.format(2/7) // '0.2857142857142857' + * math.format(math.pi, 3) // '3.14' + * math.format(new Complex(2, 3)) // '2 + 3i' + * math.format('hello') // '"hello"' + * + * @param {*} value Value to be stringified + * @param {Object | number | Function} [options] Formatting options. See + * lib/utils/number:format for a + * description of the available + * options. + * @return {string} str + */ + + +exports.format = function (value, options) { + if (typeof value === 'number') { + return formatNumber(value, options); + } + + if (isBigNumber(value)) { + return formatBigNumber(value, options); + } // note: we use unsafe duck-typing here to check for Fractions, this is + // ok here since we're only invoking toString or concatenating its values + + + if (looksLikeFraction(value)) { + if (!options || options.fraction !== 'decimal') { + // output as ratio, like '1/3' + return value.s * value.n + '/' + value.d; + } else { + // output as decimal, like '0.(3)' + return value.toString(); + } + } + + if (Array.isArray(value)) { + return formatArray(value, options); + } + + if (exports.isString(value)) { + return '"' + value + '"'; + } + + if (typeof value === 'function') { + return value.syntax ? String(value.syntax) : 'function'; + } + + if (value && _typeof(value) === 'object') { + if (typeof value.format === 'function') { + return value.format(options); + } else if (value && value.toString() !== {}.toString()) { + // this object has a non-native toString method, use that one + return value.toString(); + } else { + var entries = []; + + for (var key in value) { + if (value.hasOwnProperty(key)) { + entries.push('"' + key + '": ' + exports.format(value[key], options)); + } + } + + return '{' + entries.join(', ') + '}'; + } + } + + return String(value); +}; +/** + * Stringify a value into a string enclosed in double quotes. + * Unescaped double quotes and backslashes inside the value are escaped. + * @param {*} value + * @return {string} + */ + + +exports.stringify = function (value) { + var text = String(value); + var escaped = ''; + var i = 0; + + while (i < text.length) { + var c = text.charAt(i); + + if (c === '\\') { + escaped += c; + i++; + c = text.charAt(i); + + if (c === '' || '"\\/bfnrtu'.indexOf(c) === -1) { + escaped += '\\'; // no valid escape character -> escape it + } + + escaped += c; + } else if (c === '"') { + escaped += '\\"'; + } else { + escaped += c; + } + + i++; + } + + return '"' + escaped + '"'; +}; +/** + * Escape special HTML characters + * @param {*} value + * @return {string} + */ + + +exports.escape = function (value) { + var text = String(value); + text = text.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>'); + return text; +}; +/** + * Recursively format an n-dimensional matrix + * Example output: "[[1, 2], [3, 4]]" + * @param {Array} array + * @param {Object | number | Function} [options] Formatting options. See + * lib/utils/number:format for a + * description of the available + * options. + * @returns {string} str + */ + + +function formatArray(array, options) { + if (Array.isArray(array)) { + var str = '['; + var len = array.length; + + for (var i = 0; i < len; i++) { + if (i !== 0) { + str += ', '; + } + + str += formatArray(array[i], options); + } + + str += ']'; + return str; + } else { + return exports.format(array, options); + } +} +/** + * Check whether a value looks like a Fraction (unsafe duck-type check) + * @param {*} value + * @return {boolean} + */ + + +function looksLikeFraction(value) { + return value && _typeof(value) === 'object' && typeof value.s === 'number' && typeof value.n === 'number' && typeof value.d === 'number' || false; +} +},{"./bignumber/formatter":47,"./bignumber/isBigNumber":48,"./number":54}],58:[function(require,module,exports){ +function E () { + // Keep this empty so it's easier to inherit from + // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) +} + +E.prototype = { + on: function (name, callback, ctx) { + var e = this.e || (this.e = {}); + + (e[name] || (e[name] = [])).push({ + fn: callback, + ctx: ctx + }); + + return this; + }, + + once: function (name, callback, ctx) { + var self = this; + function listener () { + self.off(name, listener); + callback.apply(ctx, arguments); + }; + + listener._ = callback + return this.on(name, listener, ctx); + }, + + emit: function (name) { + var data = [].slice.call(arguments, 1); + var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); + var i = 0; + var len = evtArr.length; + + for (i; i < len; i++) { + evtArr[i].fn.apply(evtArr[i].ctx, data); + } + + return this; + }, + + off: function (name, callback) { + var e = this.e || (this.e = {}); + var evts = e[name]; + var liveEvents = []; + + if (evts && callback) { + for (var i = 0, len = evts.length; i < len; i++) { + if (evts[i].fn !== callback && evts[i].fn._ !== callback) + liveEvents.push(evts[i]); + } + } + + // Remove event from queue to prevent memory leak + // Suggested by https://github.com/lazd + // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 + + (liveEvents.length) + ? e[name] = liveEvents + : delete e[name]; + + return this; + } +}; + +module.exports = E; + +},{}],59:[function(require,module,exports){ +/** + * typed-function + * + * Type checking for JavaScript functions + * + * https://github.com/josdejong/typed-function + */ +'use strict'; + +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else if (typeof exports === 'object') { + // OldNode. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like OldNode. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.typed = factory(); + } +}(this, function () { + + function ok () { + return true; + } + + function notOk () { + return false; + } + + function undef () { + return undefined; + } + + /** + * @typedef {{ + * params: Param[], + * fn: function + * }} Signature + * + * @typedef {{ + * types: Type[], + * restParam: boolean + * }} Param + * + * @typedef {{ + * name: string, + * typeIndex: number, + * test: function, + * conversion?: ConversionDef, + * conversionIndex: number, + * }} Type + * + * @typedef {{ + * from: string, + * to: string, + * convert: function (*) : * + * }} ConversionDef + * + * @typedef {{ + * name: string, + * test: function(*) : boolean + * }} TypeDef + */ + + // create a new instance of typed-function + function create () { + // data type tests + var _types = [ + { name: 'number', test: function (x) { return typeof x === 'number' } }, + { name: 'string', test: function (x) { return typeof x === 'string' } }, + { name: 'boolean', test: function (x) { return typeof x === 'boolean' } }, + { name: 'Function', test: function (x) { return typeof x === 'function'} }, + { name: 'Array', test: Array.isArray }, + { name: 'Date', test: function (x) { return x instanceof Date } }, + { name: 'RegExp', test: function (x) { return x instanceof RegExp } }, + { name: 'Object', test: function (x) { + return typeof x === 'object' && x.constructor === Object + }}, + { name: 'null', test: function (x) { return x === null } }, + { name: 'undefined', test: function (x) { return x === undefined } } + ]; + + var anyType = { + name: 'any', + test: ok + } + + // types which need to be ignored + var _ignore = []; + + // type conversions + var _conversions = []; + + // This is a temporary object, will be replaced with a typed function at the end + var typed = { + types: _types, + conversions: _conversions, + ignore: _ignore + }; + + /** + * Find the test function for a type + * @param {String} typeName + * @return {TypeDef} Returns the type definition when found, + * Throws a TypeError otherwise + */ + function findTypeByName (typeName) { + var entry = findInArray(typed.types, function (entry) { + return entry.name === typeName; + }); + + if (entry) { + return entry; + } + + if (typeName === 'any') { // special baked-in case 'any' + return anyType; + } + + var hint = findInArray(typed.types, function (entry) { + return entry.name.toLowerCase() === typeName.toLowerCase(); + }); + + throw new TypeError('Unknown type "' + typeName + '"' + + (hint ? ('. Did you mean "' + hint.name + '"?') : '')); + } + + /** + * Find the index of a type definition. Handles special case 'any' + * @param {TypeDef} type + * @return {number} + */ + function findTypeIndex(type) { + if (type === anyType) { + return 999; + } + + return typed.types.indexOf(type); + } + + /** + * Find a type that matches a value. + * @param {*} value + * @return {string} Returns the name of the first type for which + * the type test matches the value. + */ + function findTypeName(value) { + var entry = findInArray(typed.types, function (entry) { + return entry.test(value); + }); + + if (entry) { + return entry.name; + } + + throw new TypeError('Value has unknown type. Value: ' + value); + } + + /** + * Find a specific signature from a (composed) typed function, for example: + * + * typed.find(fn, ['number', 'string']) + * typed.find(fn, 'number, string') + * + * Function find only only works for exact matches. + * + * @param {Function} fn A typed-function + * @param {string | string[]} signature Signature to be found, can be + * an array or a comma separated string. + * @return {Function} Returns the matching signature, or + * throws an error when no signature + * is found. + */ + function find (fn, signature) { + if (!fn.signatures) { + throw new TypeError('Function is no typed-function'); + } + + // normalize input + var arr; + if (typeof signature === 'string') { + arr = signature.split(','); + for (var i = 0; i < arr.length; i++) { + arr[i] = arr[i].trim(); + } + } + else if (Array.isArray(signature)) { + arr = signature; + } + else { + throw new TypeError('String array or a comma separated string expected'); + } + + var str = arr.join(','); + + // find an exact match + var match = fn.signatures[str]; + if (match) { + return match; + } + + // TODO: extend find to match non-exact signatures + + throw new TypeError('Signature not found (signature: ' + (fn.name || 'unnamed') + '(' + arr.join(', ') + '))'); + } + + /** + * Convert a given value to another data type. + * @param {*} value + * @param {string} type + */ + function convert (value, type) { + var from = findTypeName(value); + + // check conversion is needed + if (type === from) { + return value; + } + + for (var i = 0; i < typed.conversions.length; i++) { + var conversion = typed.conversions[i]; + if (conversion.from === from && conversion.to === type) { + return conversion.convert(value); + } + } + + throw new Error('Cannot convert from ' + from + ' to ' + type); + } + + /** + * Stringify parameters in a normalized way + * @param {Param[]} params + * @return {string} + */ + function stringifyParams (params) { + return params + .map(function (param) { + var typeNames = param.types.map(getTypeName); + + return (param.restParam ? '...' : '') + typeNames.join('|'); + }) + .join(','); + } + + /** + * Parse a parameter, like "...number | boolean" + * @param {string} param + * @param {ConversionDef[]} conversions + * @return {Param} param + */ + function parseParam (param, conversions) { + var restParam = param.indexOf('...') === 0; + var types = (!restParam) + ? param + : (param.length > 3) + ? param.slice(3) + : 'any'; + + var typeNames = types.split('|').map(trim) + .filter(notEmpty) + .filter(notIgnore); + + var matchingConversions = filterConversions(conversions, typeNames); + + var exactTypes = typeNames.map(function (typeName) { + var type = findTypeByName(typeName); + + return { + name: typeName, + typeIndex: findTypeIndex(type), + test: type.test, + conversion: null, + conversionIndex: -1 + }; + }); + + var convertibleTypes = matchingConversions.map(function (conversion) { + var type = findTypeByName(conversion.from); + + return { + name: conversion.from, + typeIndex: findTypeIndex(type), + test: type.test, + conversion: conversion, + conversionIndex: conversions.indexOf(conversion) + }; + }); + + return { + types: exactTypes.concat(convertibleTypes), + restParam: restParam + }; + } + + /** + * Parse a signature with comma separated parameters, + * like "number | boolean, ...string" + * @param {string} signature + * @param {function} fn + * @param {ConversionDef[]} conversions + * @return {Signature | null} signature + */ + function parseSignature (signature, fn, conversions) { + var params = []; + + if (signature.trim() !== '') { + params = signature + .split(',') + .map(trim) + .map(function (param, index, array) { + var parsedParam = parseParam(param, conversions); + + if (parsedParam.restParam && (index !== array.length - 1)) { + throw new SyntaxError('Unexpected rest parameter "' + param + '": ' + + 'only allowed for the last parameter'); + } + + return parsedParam; + }); + } + + if (params.some(isInvalidParam)) { + // invalid signature: at least one parameter has no types + // (they may have been filtered) + return null; + } + + return { + params: params, + fn: fn + }; + } + + /** + * Test whether a set of params contains a restParam + * @param {Param[]} params + * @return {boolean} Returns true when the last parameter is a restParam + */ + function hasRestParam(params) { + var param = last(params) + return param ? param.restParam : false; + } + + /** + * Test whether a parameter contains conversions + * @param {Param} param + * @return {boolean} Returns true when at least one of the parameters + * contains a conversion. + */ + function hasConversions(param) { + return param.types.some(function (type) { + return type.conversion != null; + }); + } + + /** + * Create a type test for a single parameter, which can have one or multiple + * types. + * @param {Param} param + * @return {function(x: *) : boolean} Returns a test function + */ + function compileTest(param) { + if (!param || param.types.length === 0) { + // nothing to do + return ok; + } + else if (param.types.length === 1) { + return findTypeByName(param.types[0].name).test; + } + else if (param.types.length === 2) { + var test0 = findTypeByName(param.types[0].name).test; + var test1 = findTypeByName(param.types[1].name).test; + return function or(x) { + return test0(x) || test1(x); + } + } + else { // param.types.length > 2 + var tests = param.types.map(function (type) { + return findTypeByName(type.name).test; + }) + return function or(x) { + for (var i = 0; i < tests.length; i++) { + if (tests[i](x)) { + return true; + } + } + return false; + } + } + } + + /** + * Create a test for all parameters of a signature + * @param {Param[]} params + * @return {function(args: Array<*>) : boolean} + */ + function compileTests(params) { + var tests, test0, test1; + + if (hasRestParam(params)) { + // variable arguments like '...number' + tests = initial(params).map(compileTest); + var varIndex = tests.length; + var lastTest = compileTest(last(params)); + var testRestParam = function (args) { + for (var i = varIndex; i < args.length; i++) { + if (!lastTest(args[i])) { + return false; + } + } + return true; + } + + return function testArgs(args) { + for (var i = 0; i < tests.length; i++) { + if (!tests[i](args[i])) { + return false; + } + } + return testRestParam(args) && (args.length >= varIndex + 1); + }; + } + else { + // no variable arguments + if (params.length === 0) { + return function testArgs(args) { + return args.length === 0; + }; + } + else if (params.length === 1) { + test0 = compileTest(params[0]); + return function testArgs(args) { + return test0(args[0]) && args.length === 1; + }; + } + else if (params.length === 2) { + test0 = compileTest(params[0]); + test1 = compileTest(params[1]); + return function testArgs(args) { + return test0(args[0]) && test1(args[1]) && args.length === 2; + }; + } + else { // arguments.length > 2 + tests = params.map(compileTest); + return function testArgs(args) { + for (var i = 0; i < tests.length; i++) { + if (!tests[i](args[i])) { + return false; + } + } + return args.length === tests.length; + }; + } + } + } + + /** + * Find the parameter at a specific index of a signature. + * Handles rest parameters. + * @param {Signature} signature + * @param {number} index + * @return {Param | null} Returns the matching parameter when found, + * null otherwise. + */ + function getParamAtIndex(signature, index) { + return index < signature.params.length + ? signature.params[index] + : hasRestParam(signature.params) + ? last(signature.params) + : null + } + + /** + * Get all type names of a parameter + * @param {Signature} signature + * @param {number} index + * @param {boolean} excludeConversions + * @return {string[]} Returns an array with type names + */ + function getExpectedTypeNames (signature, index, excludeConversions) { + var param = getParamAtIndex(signature, index); + var types = param + ? excludeConversions + ? param.types.filter(isExactType) + : param.types + : []; + + return types.map(getTypeName); + } + + /** + * Returns the name of a type + * @param {Type} type + * @return {string} Returns the type name + */ + function getTypeName(type) { + return type.name; + } + + /** + * Test whether a type is an exact type or conversion + * @param {Type} type + * @return {boolean} Returns true when + */ + function isExactType(type) { + return type.conversion === null || type.conversion === undefined; + } + + /** + * Helper function for creating error messages: create an array with + * all available types on a specific argument index. + * @param {Signature[]} signatures + * @param {number} index + * @return {string[]} Returns an array with available types + */ + function mergeExpectedParams(signatures, index) { + var typeNames = uniq(flatMap(signatures, function (signature) { + return getExpectedTypeNames(signature, index, false); + })); + + return (typeNames.indexOf('any') !== -1) ? ['any'] : typeNames; + } + + /** + * Create + * @param {string} name The name of the function + * @param {array.<*>} args The actual arguments passed to the function + * @param {Signature[]} signatures A list with available signatures + * @return {TypeError} Returns a type error with additional data + * attached to it in the property `data` + */ + function createError(name, args, signatures) { + var err, expected; + var _name = name || 'unnamed'; + + // test for wrong type at some index + var matchingSignatures = signatures; + var index; + for (index = 0; index < args.length; index++) { + var nextMatchingDefs = matchingSignatures.filter(function (signature) { + var test = compileTest(getParamAtIndex(signature, index)); + return (index < signature.params.length || hasRestParam(signature.params)) && + test(args[index]); + }); + + if (nextMatchingDefs.length === 0) { + // no matching signatures anymore, throw error "wrong type" + expected = mergeExpectedParams(matchingSignatures, index); + if (expected.length > 0) { + var actualType = findTypeName(args[index]); + + err = new TypeError('Unexpected type of argument in function ' + _name + + ' (expected: ' + expected.join(' or ') + + ', actual: ' + actualType + ', index: ' + index + ')'); + err.data = { + category: 'wrongType', + fn: _name, + index: index, + actual: actualType, + expected: expected + } + return err; + } + } + else { + matchingSignatures = nextMatchingDefs; + } + } + + // test for too few arguments + var lengths = matchingSignatures.map(function (signature) { + return hasRestParam(signature.params) ? Infinity : signature.params.length; + }); + if (args.length < Math.min.apply(null, lengths)) { + expected = mergeExpectedParams(matchingSignatures, index); + err = new TypeError('Too few arguments in function ' + _name + + ' (expected: ' + expected.join(' or ') + + ', index: ' + args.length + ')'); + err.data = { + category: 'tooFewArgs', + fn: _name, + index: args.length, + expected: expected + } + return err; + } + + // test for too many arguments + var maxLength = Math.max.apply(null, lengths); + if (args.length > maxLength) { + err = new TypeError('Too many arguments in function ' + _name + + ' (expected: ' + maxLength + ', actual: ' + args.length + ')'); + err.data = { + category: 'tooManyArgs', + fn: _name, + index: args.length, + expectedLength: maxLength + } + return err; + } + + err = new TypeError('Arguments of type "' + args.join(', ') + + '" do not match any of the defined signatures of function ' + _name + '.'); + err.data = { + category: 'mismatch', + actual: args.map(findTypeName) + } + return err; + } + + /** + * Find the lowest index of all exact types of a parameter (no conversions) + * @param {Param} param + * @return {number} Returns the index of the lowest type in typed.types + */ + function getLowestTypeIndex (param) { + var min = 999; + + for (var i = 0; i < param.types.length; i++) { + if (isExactType(param.types[i])) { + min = Math.min(min, param.types[i].typeIndex); + } + } + + return min; + } + + /** + * Find the lowest index of the conversion of all types of the parameter + * having a conversion + * @param {Param} param + * @return {number} Returns the lowest index of the conversions of this type + */ + function getLowestConversionIndex (param) { + var min = 999; + + for (var i = 0; i < param.types.length; i++) { + if (!isExactType(param.types[i])) { + min = Math.min(min, param.types[i].conversionIndex); + } + } + + return min; + } + + /** + * Compare two params + * @param {Param} param1 + * @param {Param} param2 + * @return {number} returns a negative number when param1 must get a lower + * index than param2, a positive number when the opposite, + * or zero when both are equal + */ + function compareParams (param1, param2) { + var c; + + // compare having a rest parameter or not + c = param1.restParam - param2.restParam; + if (c !== 0) { + return c; + } + + // compare having conversions or not + c = hasConversions(param1) - hasConversions(param2); + if (c !== 0) { + return c; + } + + // compare the index of the types + c = getLowestTypeIndex(param1) - getLowestTypeIndex(param2); + if (c !== 0) { + return c; + } + + // compare the index of any conversion + return getLowestConversionIndex(param1) - getLowestConversionIndex(param2); + } + + /** + * Compare two signatures + * @param {Signature} signature1 + * @param {Signature} signature2 + * @return {number} returns a negative number when param1 must get a lower + * index than param2, a positive number when the opposite, + * or zero when both are equal + */ + function compareSignatures (signature1, signature2) { + var len = Math.min(signature1.params.length, signature2.params.length); + var i; + var c; + + // compare whether the params have conversions at all or not + c = signature1.params.some(hasConversions) - signature2.params.some(hasConversions) + if (c !== 0) { + return c; + } + + // next compare whether the params have conversions one by one + for (i = 0; i < len; i++) { + c = hasConversions(signature1.params[i]) - hasConversions(signature2.params[i]); + if (c !== 0) { + return c; + } + } + + // compare the types of the params one by one + for (i = 0; i < len; i++) { + c = compareParams(signature1.params[i], signature2.params[i]); + if (c !== 0) { + return c; + } + } + + // compare the number of params + return signature1.params.length - signature2.params.length; + } + + /** + * Get params containing all types that can be converted to the defined types. + * + * @param {ConversionDef[]} conversions + * @param {string[]} typeNames + * @return {ConversionDef[]} Returns the conversions that are available + * for every type (if any) + */ + function filterConversions(conversions, typeNames) { + var matches = {}; + + conversions.forEach(function (conversion) { + if (typeNames.indexOf(conversion.from) === -1 && + typeNames.indexOf(conversion.to) !== -1 && + !matches[conversion.from]) { + matches[conversion.from] = conversion; + } + }); + + return Object.keys(matches).map(function (from) { + return matches[from]; + }); + } + + /** + * Preprocess arguments before calling the original function: + * - if needed convert the parameters + * - in case of rest parameters, move the rest parameters into an Array + * @param {Param[]} params + * @param {function} fn + * @return {function} Returns a wrapped function + */ + function compileArgsPreprocessing(params, fn) { + var fnConvert = fn; + + // TODO: can we make this wrapper function smarter/simpler? + + if (params.some(hasConversions)) { + var restParam = hasRestParam(params); + var compiledConversions = params.map(compileArgConversion) + + fnConvert = function convertArgs() { + var args = []; + var last = restParam ? arguments.length - 1 : arguments.length; + for (var i = 0; i < last; i++) { + args[i] = compiledConversions[i](arguments[i]); + } + if (restParam) { + args[last] = arguments[last].map(compiledConversions[last]); + } + + return fn.apply(null, args); + } + } + + var fnPreprocess = fnConvert; + if (hasRestParam(params)) { + var offset = params.length - 1; + + fnPreprocess = function preprocessRestParams () { + return fnConvert.apply(null, + slice(arguments, 0, offset).concat([slice(arguments, offset)])); + } + } + + return fnPreprocess; + } + + /** + * Compile conversion for a parameter to the right type + * @param {Param} param + * @return {function} Returns the wrapped function that will convert arguments + * + */ + function compileArgConversion(param) { + var test0, test1, conversion0, conversion1; + var tests = []; + var conversions = []; + + param.types.forEach(function (type) { + if (type.conversion) { + tests.push(findTypeByName(type.conversion.from).test); + conversions.push(type.conversion.convert); + } + }); + + // create optimized conversion functions depending on the number of conversions + switch (conversions.length) { + case 0: + return function convertArg(arg) { + return arg; + } + + case 1: + test0 = tests[0] + conversion0 = conversions[0]; + return function convertArg(arg) { + if (test0(arg)) { + return conversion0(arg) + } + return arg; + } + + case 2: + test0 = tests[0] + test1 = tests[1] + conversion0 = conversions[0]; + conversion1 = conversions[1]; + return function convertArg(arg) { + if (test0(arg)) { + return conversion0(arg) + } + if (test1(arg)) { + return conversion1(arg) + } + return arg; + } + + default: + return function convertArg(arg) { + for (var i = 0; i < conversions.length; i++) { + if (tests[i](arg)) { + return conversions[i](arg); + } + } + return arg; + } + } + } + + /** + * Convert an array with signatures into a map with signatures, + * where signatures with union types are split into separate signatures + * + * Throws an error when there are conflicting types + * + * @param {Signature[]} signatures + * @return {Object.<string, function>} Returns a map with signatures + * as key and the original function + * of this signature as value. + */ + function createSignaturesMap(signatures) { + var signaturesMap = {}; + signatures.forEach(function (signature) { + if (!signature.params.some(hasConversions)) { + splitParams(signature.params, true).forEach(function (params) { + signaturesMap[stringifyParams(params)] = signature.fn; + }); + } + }); + + return signaturesMap; + } + + /** + * Split params with union types in to separate params. + * + * For example: + * + * splitParams([['Array', 'Object'], ['string', 'RegExp']) + * // returns: + * // [ + * // ['Array', 'string'], + * // ['Array', 'RegExp'], + * // ['Object', 'string'], + * // ['Object', 'RegExp'] + * // ] + * + * @param {Param[]} params + * @param {boolean} ignoreConversionTypes + * @return {Param[]} + */ + function splitParams(params, ignoreConversionTypes) { + function _splitParams(params, index, types) { + if (index < params.length) { + var param = params[index] + var filteredTypes = ignoreConversionTypes + ? param.types.filter(isExactType) + : param.types; + var typeGroups + + if (param.restParam) { + // split the types of a rest parameter in two: + // one with only exact types, and one with exact types and conversions + var exactTypes = filteredTypes.filter(isExactType) + typeGroups = exactTypes.length < filteredTypes.length + ? [exactTypes, filteredTypes] + : [filteredTypes] + + } + else { + // split all the types of a regular parameter into one type per group + typeGroups = filteredTypes.map(function (type) { + return [type] + }) + } + + // recurse over the groups with types + return flatMap(typeGroups, function (typeGroup) { + return _splitParams(params, index + 1, types.concat([typeGroup])); + }); + + } + else { + // we've reached the end of the parameters. Now build a new Param + var splittedParams = types.map(function (type, typeIndex) { + return { + types: type, + restParam: (typeIndex === params.length - 1) && hasRestParam(params) + } + }); + + return [splittedParams]; + } + } + + return _splitParams(params, 0, []); + } + + /** + * Test whether two signatures have a conflicting signature + * @param {Signature} signature1 + * @param {Signature} signature2 + * @return {boolean} Returns true when the signatures conflict, false otherwise. + */ + function hasConflictingParams(signature1, signature2) { + var ii = Math.max(signature1.params.length, signature2.params.length); + + for (var i = 0; i < ii; i++) { + var typesNames1 = getExpectedTypeNames(signature1, i, true); + var typesNames2 = getExpectedTypeNames(signature2, i, true); + + if (!hasOverlap(typesNames1, typesNames2)) { + return false; + } + } + + var len1 = signature1.params.length; + var len2 = signature2.params.length; + var restParam1 = hasRestParam(signature1.params); + var restParam2 = hasRestParam(signature2.params); + + return restParam1 + ? restParam2 ? (len1 === len2) : (len2 >= len1) + : restParam2 ? (len1 >= len2) : (len1 === len2) + } + + /** + * Create a typed function + * @param {String} name The name for the typed function + * @param {Object.<string, function>} signaturesMap + * An object with one or + * multiple signatures as key, and the + * function corresponding to the + * signature as value. + * @return {function} Returns the created typed function. + */ + function createTypedFunction(name, signaturesMap) { + if (Object.keys(signaturesMap).length === 0) { + throw new SyntaxError('No signatures provided'); + } + + // parse the signatures, and check for conflicts + var parsedSignatures = []; + Object.keys(signaturesMap) + .map(function (signature) { + return parseSignature(signature, signaturesMap[signature], typed.conversions); + }) + .filter(notNull) + .forEach(function (parsedSignature) { + // check whether this parameter conflicts with already parsed signatures + var conflictingSignature = findInArray(parsedSignatures, function (s) { + return hasConflictingParams(s, parsedSignature) + }); + if (conflictingSignature) { + throw new TypeError('Conflicting signatures "' + + stringifyParams(conflictingSignature.params) + '" and "' + + stringifyParams(parsedSignature.params) + '".'); + } + + parsedSignatures.push(parsedSignature); + }); + + // split and filter the types of the signatures, and then order them + var signatures = flatMap(parsedSignatures, function (parsedSignature) { + var params = parsedSignature ? splitParams(parsedSignature.params, false) : [] + + return params.map(function (params) { + return { + params: params, + fn: parsedSignature.fn + }; + }); + }).filter(notNull); + + signatures.sort(compareSignatures); + + // we create a highly optimized checks for the first couple of signatures with max 2 arguments + var ok0 = signatures[0] && signatures[0].params.length <= 2 && !hasRestParam(signatures[0].params); + var ok1 = signatures[1] && signatures[1].params.length <= 2 && !hasRestParam(signatures[1].params); + var ok2 = signatures[2] && signatures[2].params.length <= 2 && !hasRestParam(signatures[2].params); + var ok3 = signatures[3] && signatures[3].params.length <= 2 && !hasRestParam(signatures[3].params); + var ok4 = signatures[4] && signatures[4].params.length <= 2 && !hasRestParam(signatures[4].params); + var ok5 = signatures[5] && signatures[5].params.length <= 2 && !hasRestParam(signatures[5].params); + var allOk = ok0 && ok1 && ok2 && ok3 && ok4 && ok5; + + // compile the tests + var tests = signatures.map(function (signature) { + return compileTests(signature.params); + }); + + var test00 = ok0 ? compileTest(signatures[0].params[0]) : notOk; + var test10 = ok1 ? compileTest(signatures[1].params[0]) : notOk; + var test20 = ok2 ? compileTest(signatures[2].params[0]) : notOk; + var test30 = ok3 ? compileTest(signatures[3].params[0]) : notOk; + var test40 = ok4 ? compileTest(signatures[4].params[0]) : notOk; + var test50 = ok5 ? compileTest(signatures[5].params[0]) : notOk; + + var test01 = ok0 ? compileTest(signatures[0].params[1]) : notOk; + var test11 = ok1 ? compileTest(signatures[1].params[1]) : notOk; + var test21 = ok2 ? compileTest(signatures[2].params[1]) : notOk; + var test31 = ok3 ? compileTest(signatures[3].params[1]) : notOk; + var test41 = ok4 ? compileTest(signatures[4].params[1]) : notOk; + var test51 = ok5 ? compileTest(signatures[5].params[1]) : notOk; + + // compile the functions + var fns = signatures.map(function(signature) { + return compileArgsPreprocessing(signature.params, signature.fn) + }); + + var fn0 = ok0 ? fns[0] : undef; + var fn1 = ok1 ? fns[1] : undef; + var fn2 = ok2 ? fns[2] : undef; + var fn3 = ok3 ? fns[3] : undef; + var fn4 = ok4 ? fns[4] : undef; + var fn5 = ok5 ? fns[5] : undef; + + var len0 = ok0 ? signatures[0].params.length : -1; + var len1 = ok1 ? signatures[1].params.length : -1; + var len2 = ok2 ? signatures[2].params.length : -1; + var len3 = ok3 ? signatures[3].params.length : -1; + var len4 = ok4 ? signatures[4].params.length : -1; + var len5 = ok5 ? signatures[5].params.length : -1; + + // simple and generic, but also slow + var iStart = allOk ? 6 : 0; + var iEnd = signatures.length; + var generic = function generic() { + 'use strict'; + + for (var i = iStart; i < iEnd; i++) { + if (tests[i](arguments)) { + return fns[i].apply(null, arguments); + } + } + + throw createError(name, arguments, signatures); + } + + // create the typed function + // fast, specialized version. Falls back to the slower, generic one if needed + var fn = function fn(arg0, arg1) { + 'use strict'; + + if (arguments.length === len0 && test00(arg0) && test01(arg1)) { return fn0.apply(null, arguments); } + if (arguments.length === len1 && test10(arg0) && test11(arg1)) { return fn1.apply(null, arguments); } + if (arguments.length === len2 && test20(arg0) && test21(arg1)) { return fn2.apply(null, arguments); } + if (arguments.length === len3 && test30(arg0) && test31(arg1)) { return fn3.apply(null, arguments); } + if (arguments.length === len4 && test40(arg0) && test41(arg1)) { return fn4.apply(null, arguments); } + if (arguments.length === len5 && test50(arg0) && test51(arg1)) { return fn5.apply(null, arguments); } + + return generic.apply(null, arguments); + } + + // attach name the typed function + try { + Object.defineProperty(fn, 'name', {value: name}); + } + catch (err) { + // old browsers do not support Object.defineProperty and some don't support setting the name property + // the function name is not essential for the functioning, it's mostly useful for debugging, + // so it's fine to have unnamed functions. + } + + // attach signatures to the function + fn.signatures = createSignaturesMap(signatures); + + return fn; + } + + /** + * Test whether a type should be NOT be ignored + * @param {string} typeName + * @return {boolean} + */ + function notIgnore(typeName) { + return typed.ignore.indexOf(typeName) === -1; + } + + /** + * trim a string + * @param {string} str + * @return {string} + */ + function trim(str) { + return str.trim(); + } + + /** + * Test whether a string is not empty + * @param {string} str + * @return {boolean} + */ + function notEmpty(str) { + return !!str; + } + + /** + * test whether a value is not strict equal to null + * @param {*} value + * @return {boolean} + */ + function notNull(value) { + return value !== null; + } + + /** + * Test whether a parameter has no types defined + * @param {Param} param + * @return {boolean} + */ + function isInvalidParam (param) { + return param.types.length === 0; + } + + /** + * Return all but the last items of an array + * @param {Array} arr + * @return {Array} + */ + function initial(arr) { + return arr.slice(0, arr.length - 1); + } + + /** + * return the last item of an array + * @param {Array} arr + * @return {*} + */ + function last(arr) { + return arr[arr.length - 1]; + } + + /** + * Slice an array or function Arguments + * @param {Array | Arguments | IArguments} arr + * @param {number} start + * @param {number} [end] + * @return {Array} + */ + function slice(arr, start, end) { + return Array.prototype.slice.call(arr, start, end); + } + + /** + * Test whether an array contains some item + * @param {Array} array + * @param {*} item + * @return {boolean} Returns true if array contains item, false if not. + */ + function contains(array, item) { + return array.indexOf(item) !== -1; + } + + /** + * Test whether two arrays have overlapping items + * @param {Array} array1 + * @param {Array} array2 + * @return {boolean} Returns true when at least one item exists in both arrays + */ + function hasOverlap(array1, array2) { + for (var i = 0; i < array1.length; i++) { + if (contains(array2, array1[i])) { + return true; + } + } + + return false; + } + + /** + * Return the first item from an array for which test(arr[i]) returns true + * @param {Array} arr + * @param {function} test + * @return {* | undefined} Returns the first matching item + * or undefined when there is no match + */ + function findInArray(arr, test) { + for (var i = 0; i < arr.length; i++) { + if (test(arr[i])) { + return arr[i]; + } + } + return undefined; + } + + /** + * Filter unique items of an array with strings + * @param {string[]} arr + * @return {string[]} + */ + function uniq(arr) { + var entries = {} + for (var i = 0; i < arr.length; i++) { + entries[arr[i]] = true; + } + return Object.keys(entries); + } + + /** + * Flat map the result invoking a callback for every item in an array. + * https://gist.github.com/samgiles/762ee337dff48623e729 + * @param {Array} arr + * @param {function} callback + * @return {Array} + */ + function flatMap(arr, callback) { + return Array.prototype.concat.apply([], arr.map(callback)); + } + + /** + * Retrieve the function name from a set of typed functions, + * and check whether the name of all functions match (if given) + * @param {function[]} fns + */ + function getName (fns) { + var name = ''; + + for (var i = 0; i < fns.length; i++) { + var fn = fns[i]; + + // check whether the names are the same when defined + if ((typeof fn.signatures === 'object' || typeof fn.signature === 'string') && fn.name !== '') { + if (name === '') { + name = fn.name; + } + else if (name !== fn.name) { + var err = new Error('Function names do not match (expected: ' + name + ', actual: ' + fn.name + ')'); + err.data = { + actual: fn.name, + expected: name + }; + throw err; + } + } + } + + return name; + } + + // extract and merge all signatures of a list with typed functions + function extractSignatures(fns) { + var err; + var signaturesMap = {}; + + function validateUnique(_signature, _fn) { + if (signaturesMap.hasOwnProperty(_signature) && _fn !== signaturesMap[_signature]) { + err = new Error('Signature "' + _signature + '" is defined twice'); + err.data = {signature: _signature}; + throw err; + // else: both signatures point to the same function, that's fine + } + } + + for (var i = 0; i < fns.length; i++) { + var fn = fns[i]; + + // test whether this is a typed-function + if (typeof fn.signatures === 'object') { + // merge the signatures + for (var signature in fn.signatures) { + if (fn.signatures.hasOwnProperty(signature)) { + validateUnique(signature, fn.signatures[signature]); + signaturesMap[signature] = fn.signatures[signature]; + } + } + } + else if (typeof fn.signature === 'string') { + validateUnique(fn.signature, fn); + signaturesMap[fn.signature] = fn; + } + else { + err = new TypeError('Function is no typed-function (index: ' + i + ')'); + err.data = {index: i}; + throw err; + } + } + + return signaturesMap; + } + + typed = createTypedFunction('typed', { + 'string, Object': createTypedFunction, + 'Object': function (signaturesMap) { + // find existing name + var fns = []; + for (var signature in signaturesMap) { + if (signaturesMap.hasOwnProperty(signature)) { + fns.push(signaturesMap[signature]); + } + } + var name = getName(fns); + return createTypedFunction(name, signaturesMap); + }, + '...Function': function (fns) { + return createTypedFunction(getName(fns), extractSignatures(fns)); + }, + 'string, ...Function': function (name, fns) { + return createTypedFunction(name, extractSignatures(fns)); + } + }); + + typed.create = create; + typed.types = _types; + typed.conversions = _conversions; + typed.ignore = _ignore; + typed.convert = convert; + typed.find = find; + + /** + * add a type + * @param {{name: string, test: function}} type + * @param {boolean} [beforeObjectTest=true] + * If true, the new test will be inserted before + * the test with name 'Object' (if any), since + * tests for Object match Array and classes too. + */ + typed.addType = function (type, beforeObjectTest) { + if (!type || typeof type.name !== 'string' || typeof type.test !== 'function') { + throw new TypeError('Object with properties {name: string, test: function} expected'); + } + + if (beforeObjectTest !== false) { + for (var i = 0; i < typed.types.length; i++) { + if (typed.types[i].name === 'Object') { + typed.types.splice(i, 0, type); + return + } + } + } + + typed.types.push(type); + }; + + // add a conversion + typed.addConversion = function (conversion) { + if (!conversion + || typeof conversion.from !== 'string' + || typeof conversion.to !== 'string' + || typeof conversion.convert !== 'function') { + throw new TypeError('Object with properties {from: string, to: string, convert: function} expected'); + } + + typed.conversions.push(conversion); + }; + + return typed; + } + + return create(); +})); +},{}]},{},[1])(1) +}); |