Merge branch 'v4_constant_node' into v4_binary_operator_node

This commit is contained in:
jos 2018-01-31 10:07:10 +01:00
commit ae8f61a84b
31 changed files with 376 additions and 449 deletions

View File

@ -24,6 +24,14 @@ Breaking changes:
need to sort an array with text. See #680.
- `null` is no longer implicitly casted to a number `0`, so input like
`math.add(2, null)` is no longer supported. See #830, #353.
- The class `ConstantNode` is changed such that it just holds a value
instead of holding a stringified value and it's type.
`ConstantNode(valueStr, valueType`) is now `ConstantNode(value)`
Stringification uses `math.format`, which may result in differently
formatted numeric output.
- The constants `true`, `false`, `null`, `undefined`, `NaN`, `Infinity`,
and `uninitialized` are now parsed as ConstantNodes instead of
SymbolNodes in the expression parser. See #833.
- In function `math.format`, the option `notation: 'fixed'` no longer rounds to
zero digits when no precision is specified: it leaves the digits as is.
See #676.

View File

@ -405,13 +405,12 @@ var node2 = new math.expression.node.ConditionalNode(condition, trueExpr, fa
Construction:
```
new ConstantNode(value: * [, valueType: string])
new ConstantNode(value: *)
```
Properties:
- `value: *`
- `valueType: string`
Examples:
@ -419,7 +418,7 @@ Examples:
var node1 = math.parse('2.4');
var node2 = new math.expression.node.ConstantNode(2.4);
var node3 = new math.expression.node.ConstantNode('2.4', 'number');
var node3 = new math.expression.node.ConstantNode('foo');
```

View File

@ -1,79 +1,36 @@
'use strict';
var getType = require('../../utils/types').type;
var stringify = require('../../utils/string').stringify;
var escape = require('../../utils/string').escape;
var format = require('../../utils/string').format;
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. A ConstantNode
* stores a stringified version of the value and uses this to compile to
* JavaScript.
*
* In case of a stringified number as input, this may be compiled to a BigNumber
* when the math instance is configured for BigNumbers.
* A ConstantNode holds a constant value like a number or string.
*
* Usage:
*
* // stringified values with type
* new ConstantNode('2.3', 'number');
* new ConstantNode('true', 'boolean');
* new ConstantNode('hello', 'string');
*
* // non-stringified values, type will be automatically detected
* new ConstantNode(2.3);
* new ConstantNode('hello');
*
* @param {string | number | boolean | null | undefined} value
* When valueType is provided, value must contain
* an uninterpreted string representing the value.
* When valueType is undefined, value can be a
* number, string, boolean, null, or undefined, and
* the type will be determined automatically.
* @param {string} [valueType] The type of value. Choose from 'number', 'string',
* 'boolean', 'undefined', 'null'
* @param {*} value Value can be any type (number, BigNumber, string, ...)
* @constructor ConstantNode
* @extends {Node}
*/
function ConstantNode(value, valueType) {
// TODO: make the whole valueType redundant, simply parse numbers in parse.js already
function ConstantNode(value) {
if (!(this instanceof ConstantNode)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
if (valueType) {
if (typeof valueType !== 'string') {
throw new TypeError('String expected for parameter "valueType"');
}
if (typeof value !== 'string') {
throw new TypeError('String expected for parameter "value"');
}
this.value = value;
this.valueType = valueType;
}
else {
// stringify the value and determine the type
this.value = value + '';
this.valueType = getType(value);
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.');
}
if (!SUPPORTED_TYPES[this.valueType]) {
throw new TypeError('Unsupported type of value "' + this.valueType + '"');
}
this.value = value;
}
var SUPPORTED_TYPES = {
'number': true,
'string': true,
'boolean': true,
'undefined': true,
'null': true
};
ConstantNode.prototype = new Node();
ConstantNode.prototype.type = 'ConstantNode';
@ -94,74 +51,13 @@ function factory (type, config, load, typed) {
* evalNode(scope: Object, args: Object, context: *)
*/
ConstantNode.prototype._compile = function (math, argNames) {
var value;
var value = this.value;
switch (this.valueType) {
case 'number':
if (config.number === 'BigNumber') {
value = new type.BigNumber(this.value);
return function evalConstantNode() {
return value;
}
}
else if (config.number === 'Fraction') {
value = new type.Fraction(this.value);
return function evalConstantNode() {
return value;
}
}
else {
// remove leading zeros like '003.2' which are not allowed by JavaScript
validateNumericValue(this.value);
value = parseFloat(this.value.replace(/^(0*)[0-9]/, function (match, zeros) {
return match.substring(zeros.length);
}));
return function evalConstantNode() {
return value;
}
}
case 'string':
value = this.value;
return function evalConstantNode() {
return value;
};
case 'boolean':
// prevent invalid values
return String(this.value) === 'true'
? function () { return true; }
: function () { return false; };
case 'undefined':
return function evalConstantNode() {
return undefined;
};
case 'null':
return function evalConstantNode() {
return null;
};
default:
// TODO: move this error to the constructor?
throw new TypeError('Unsupported type of constant "' + this.valueType + '"');
return function evalConstantNode() {
return value;
}
}
/**
* Test whether value is a string containing a numeric value
* @param {String} value
* @return {boolean} Returns true when ok
*/
function validateNumericValue (value) {
// The following regexp is relatively permissive
if (typeof value !== 'string' ||
!/^[\-+]?((\d+\.?\d*)|(\d*\.?\d+))([eE][+\-]?\d+)?$/.test(value)) {
throw new Error('Invalid numeric value "' + value + '"');
}
}
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
@ -170,7 +66,6 @@ function factory (type, config, load, typed) {
// 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.
@ -186,7 +81,7 @@ function factory (type, config, load, typed) {
* @return {ConstantNode}
*/
ConstantNode.prototype.clone = function () {
return new ConstantNode(this.value, this.valueType);
return new ConstantNode(this.value);
};
/**
@ -195,13 +90,7 @@ function factory (type, config, load, typed) {
* @return {string} str
*/
ConstantNode.prototype._toString = function (options) {
switch (this.valueType) {
case 'string':
return stringify(this.value);
default:
return this.value;
}
return format (this.value, options);
};
/**
@ -210,9 +99,12 @@ function factory (type, config, load, typed) {
* @return {string} str
*/
ConstantNode.prototype.toHTML = function (options) {
var value = escape(this.value);
switch (this.valueType) {
case 'number':
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>';
@ -234,20 +126,24 @@ function factory (type, config, load, typed) {
* @return {string} str
*/
ConstantNode.prototype._toTex = function (options) {
var value = this.value,
index;
switch (this.valueType) {
var value = this._toString(options);
switch (getType(this.value)) {
case 'string':
return '\\mathtt{' + stringify(value) + '}';
return '\\mathtt{' + value + '}';
case 'number':
index = value.toLowerCase().indexOf('e');
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;
}

View File

@ -188,7 +188,7 @@ function factory (type, config, load, typed) {
IndexNode.prototype.isObjectProperty = function () {
return this.dimensions.length === 1 &&
type.isConstantNode(this.dimensions[0]) &&
this.dimensions[0].valueType === 'string';
typeof this.dimensions[0].value === 'string';
};
/**

View File

@ -4,6 +4,9 @@ var ArgumentsError = require('../error/ArgumentsError');
var deepMap = require('../utils/collection/deepMap');
function factory (type, config, load, typed) {
var numeric = load(require('../type/numeric'));
var uninitialized = require('../utils/array').UNINITIALIZED
var AccessorNode = load(require('./node/AccessorNode'));
var ArrayNode = load(require('./node/ArrayNode'));
var AssignmentNode = load(require('./node/AssignmentNode'));
@ -19,7 +22,6 @@ function factory (type, config, load, typed) {
var RangeNode = load(require('./node/RangeNode'));
var SymbolNode = load(require('./node/SymbolNode'));
/**
* Parse an expression. Returns a node tree, which can be evaluated by
* invoking node.eval();
@ -143,6 +145,19 @@ function factory (type, config, load, typed) {
'not': true
};
var CONSTANTS = {
'true': true,
'false': false,
'null': null,
'undefined': undefined,
'uninitialized': uninitialized
}
var NUMERIC_CONSTANTS = [
'NaN',
'Infinity'
]
var extra_nodes = {}; // current extra nodes
var expression = ''; // current expression
var comment = ''; // last parsed comment
@ -557,7 +572,7 @@ function factory (type, config, load, typed) {
}
else {
if (!node) {
node = new ConstantNode('undefined', 'undefined');
node = new ConstantNode(undefined);
node.comment = comment;
}
@ -858,7 +873,7 @@ function factory (type, config, load, typed) {
if (token === ':') {
// implicit start=1 (one-based)
node = new ConstantNode('1', 'number');
node = new ConstantNode(1);
}
else {
// explicit start
@ -1136,8 +1151,17 @@ function factory (type, config, load, typed) {
getToken();
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));
}
else {
node = new SymbolNode(name);
}
// parse function parameters and matrix index
node = new SymbolNode(name);
node = parseAccessors(node);
return node;
}
@ -1250,7 +1274,7 @@ function factory (type, config, load, typed) {
str = parseStringToken();
// create constant
node = new ConstantNode(str, 'string');
node = new ConstantNode(str);
// parse index parameters
node = parseAccessors(node);
@ -1439,14 +1463,14 @@ function factory (type, config, load, typed) {
* @private
*/
function parseNumber () {
var number;
var numberStr;
if (token_type === TOKENTYPE.NUMBER) {
// this is a number
number = token;
numberStr = token;
getToken();
return new ConstantNode(number, 'number');
return new ConstantNode(numeric(numberStr, config.number));
}
return parseParentheses();

View File

@ -3,6 +3,9 @@
function factory (type, config, load, typed) {
var parse = load(require('../../expression/parse'));
var simplify = load(require('./simplify'));
var equal = load(require('../relational/equal'));
var isZero = load(require('../utils/isZero'));
var numeric = load(require('../../type/numeric'));
var ConstantNode = load(require('../../expression/node/ConstantNode'));
var FunctionNode = load(require('../../expression/node/FunctionNode'));
var OperatorNode = load(require('../../expression/node/OperatorNode'));
@ -142,7 +145,7 @@ function factory (type, config, load, typed) {
'Object, SymbolNode, string': function (constNodes, node, varName) {
// Treat other variables like constants. For reasoning, see:
// https://en.wikipedia.org/wiki/Partial_derivative
if (node.name != varName) {
if (node.name !== varName) {
return constNodes[node] = true;
}
return false;
@ -153,14 +156,14 @@ function factory (type, config, load, typed) {
},
'Object, FunctionAssignmentNode, string': function (constNodes, node, varName) {
if (node.params.indexOf(varName) == -1) {
if (node.params.indexOf(varName) === -1) {
return constNodes[node] = true;
}
return constTag(constNodes, node.expr, varName);
},
'Object, FunctionNode | OperatorNode, string': function (constNodes, node, varName) {
if (node.args.length != 0) {
if (node.args.length !== 0) {
var isConst = constTag(constNodes, node.args[0], varName);
for (var i = 1; i < node.args.length; ++i) {
isConst = constTag(constNodes, node.args[i], varName) && isConst;
@ -183,14 +186,14 @@ function factory (type, config, load, typed) {
*/
var _derivative = typed('_derivative', {
'ConstantNode, Object': function (node) {
return new ConstantNode('0', node.valueType);
return createConstantNode(0);
},
'SymbolNode, Object': function (node, constNodes) {
if (constNodes[node] !== undefined) {
return new ConstantNode('0', config.number);
return createConstantNode(0);
}
return new ConstantNode('1', config.number);
return createConstantNode(1);
},
'ParenthesisNode, Object': function (node, constNodes) {
@ -199,18 +202,18 @@ function factory (type, config, load, typed) {
'FunctionAssignmentNode, Object': function (node, constNodes) {
if (constNodes[node] !== undefined) {
return new ConstantNode('0', config.number);
return createConstantNode(0);
}
return _derivative(node.expr, constNodes);
},
'FunctionNode, Object': function (node, constNodes) {
if (node.args.length != 1) {
if (node.args.length !== 1) {
funcArgsCheck(node);
}
if (constNodes[node] !== undefined) {
return new ConstantNode('0', config.number);
return createConstantNode(0);
}
var arg1 = node.args[0];
@ -225,12 +228,12 @@ function factory (type, config, load, typed) {
// d/dx(cbrt(x)) = 1 / (3x^(2/3))
div = true;
funcDerivative = new OperatorNode('*', 'multiply', [
new ConstantNode('3', config.number),
createConstantNode(3),
new OperatorNode('^', 'pow', [
arg1,
new OperatorNode('/', 'divide', [
new ConstantNode('2', config.number),
new ConstantNode('3', config.number)
createConstantNode(2),
createConstantNode(3)
])
])
]);
@ -238,10 +241,10 @@ function factory (type, config, load, typed) {
case 'sqrt':
case 'nthRoot':
// d/dx(sqrt(x)) = 1 / (2*sqrt(x))
if (node.args.length == 1) {
if (node.args.length === 1) {
div = true;
funcDerivative = new OperatorNode('*', 'multiply', [
new ConstantNode('2', config.number),
createConstantNode(2),
new FunctionNode('sqrt', [arg1])
]);
break;
@ -249,7 +252,7 @@ function factory (type, config, load, typed) {
// Rearrange from nthRoot(x, a) -> x^(1/a)
arg2 = new OperatorNode('/', 'divide', [
new ConstantNode('1', config.number),
createConstantNode(1),
node.args[1]
]);
@ -258,9 +261,10 @@ function factory (type, config, load, typed) {
return _derivative(new OperatorNode('^', 'pow', [arg1, arg2]), constNodes);
case 'log10':
arg2 = new ConstantNode('10', config.number);
arg2 = createConstantNode(10);
/* fall through! */
case 'log':
if (!arg2 && node.args.length == 1) {
if (!arg2 && node.args.length === 1) {
// d/dx(log(x)) = 1 / x
funcDerivative = arg1.clone();
} else if (arg2 || constNodes[node.args[1]] !== undefined) {
@ -297,7 +301,7 @@ function factory (type, config, load, typed) {
// d/dx(tan(x)) = sec(x)^2
funcDerivative = new OperatorNode('^', 'pow', [
new FunctionNode('sec', [arg1.clone()]),
new ConstantNode('2', config.number)
createConstantNode(2)
]);
break;
case 'sec':
@ -320,7 +324,7 @@ function factory (type, config, load, typed) {
negative = true;
funcDerivative = new OperatorNode('^', 'pow', [
new FunctionNode('csc', [arg1.clone()]),
new ConstantNode('2', config.number)
createConstantNode(2)
]);
break;
case 'asin':
@ -328,10 +332,10 @@ function factory (type, config, load, typed) {
div = true;
funcDerivative = new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
createConstantNode(1),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
])
])
]);
@ -342,10 +346,10 @@ function factory (type, config, load, typed) {
negative = true;
funcDerivative = new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
createConstantNode(1),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
])
])
]);
@ -356,9 +360,9 @@ function factory (type, config, load, typed) {
funcDerivative = new OperatorNode('+', 'add', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
]),
new ConstantNode('1', config.number)
createConstantNode(1)
]);
break;
case 'asec':
@ -370,9 +374,9 @@ function factory (type, config, load, typed) {
new OperatorNode('-', 'subtract', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
]),
new ConstantNode('1', config.number)
createConstantNode(1)
])
])
]);
@ -387,9 +391,9 @@ function factory (type, config, load, typed) {
new OperatorNode('-', 'subtract', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
]),
new ConstantNode('1', config.number)
createConstantNode(1)
])
])
]);
@ -401,9 +405,9 @@ function factory (type, config, load, typed) {
funcDerivative = new OperatorNode('+', 'add', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
]),
new ConstantNode('1', config.number)
createConstantNode(1)
]);
break;
case 'sinh':
@ -418,7 +422,7 @@ function factory (type, config, load, typed) {
// d/dx(tanh(x)) = sech(x)^2
funcDerivative = new OperatorNode('^', 'pow', [
new FunctionNode('sech', [arg1.clone()]),
new ConstantNode('2', config.number)
createConstantNode(2)
]);
break;
case 'sech':
@ -442,7 +446,7 @@ function factory (type, config, load, typed) {
negative = true;
funcDerivative = new OperatorNode('^', 'pow', [
new FunctionNode('csch', [arg1.clone()]),
new ConstantNode('2', config.number)
createConstantNode(2)
]);
break;
case 'asinh':
@ -452,9 +456,9 @@ function factory (type, config, load, typed) {
new OperatorNode('+', 'add', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
]),
new ConstantNode('1', config.number)
createConstantNode(1)
])
]);
break;
@ -465,9 +469,9 @@ function factory (type, config, load, typed) {
new OperatorNode('-', 'subtract', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
]),
new ConstantNode('1', config.number),
createConstantNode(1)
])
]);
break;
@ -475,10 +479,10 @@ function factory (type, config, load, typed) {
// d/dx(atanh(x)) = 1 / (1 - x^2)
div = true;
funcDerivative = new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
createConstantNode(1),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
])
]);
break;
@ -490,10 +494,10 @@ function factory (type, config, load, typed) {
arg1.clone(),
new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
createConstantNode(1),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
])
])
])
@ -509,9 +513,9 @@ function factory (type, config, load, typed) {
new OperatorNode('+', 'add', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
]),
new ConstantNode('1', config.number)
createConstantNode(1)
])
])
]);
@ -521,10 +525,10 @@ function factory (type, config, load, typed) {
div = true;
negative = true;
funcDerivative = new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
createConstantNode(1),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
createConstantNode(2)
])
]);
break;
@ -560,7 +564,7 @@ function factory (type, config, load, typed) {
'OperatorNode, Object': function (node, constNodes) {
if (constNodes[node] !== undefined) {
return new ConstantNode('0', config.number);
return createConstantNode(0);
}
var arg1 = node.args[0];
@ -574,7 +578,7 @@ function factory (type, config, load, typed) {
}));
case '-':
// d/dx(+/-f(x)) = +/-f'(x)
if (node.args.length == 1) {
if (node.args.length === 1) {
return new OperatorNode(node.op, node.fn, [_derivative(arg1, constNodes)]);
}
@ -623,7 +627,7 @@ function factory (type, config, load, typed) {
new OperatorNode('-', 'unaryMinus', [arg1]),
new OperatorNode('/', 'divide', [
_derivative(arg2, constNodes),
new OperatorNode('^', 'pow', [arg2.clone(), new ConstantNode('2', config.number)])
new OperatorNode('^', 'pow', [arg2.clone(), createConstantNode(2)])
])
]);
}
@ -634,13 +638,13 @@ function factory (type, config, load, typed) {
new OperatorNode('*', 'multiply', [_derivative(arg1, constNodes), arg2.clone()]),
new OperatorNode('*', 'multiply', [arg1.clone(), _derivative(arg2, constNodes)])
]),
new OperatorNode('^', 'pow', [arg2.clone(), new ConstantNode('2', config.number)])
new OperatorNode('^', 'pow', [arg2.clone(), createConstantNode(2)])
]);
case '^':
if (constNodes[arg1] !== undefined) {
// If is secretly constant; 0^f(x) = 1 (in JS), 1^f(x) = 1
if (type.isConstantNode(arg1) && (arg1.value === '0' || arg1.value === '1')) {
return new ConstantNode('0', config.number);
if (type.isConstantNode(arg1) && (isZero(arg1.value) || equal(arg1.value, 1))) {
return createConstantNode(0);
}
// d/dx(c^f(x)) = c^f(x)*ln(c)*f'(x)
@ -655,14 +659,12 @@ function factory (type, config, load, typed) {
if (constNodes[arg2] !== undefined) {
if (type.isConstantNode(arg2)) {
var expValue = arg2.value;
// If is secretly constant; f(x)^0 = 1 -> d/dx(1) = 0
if (expValue === '0') {
return new ConstantNode('0', config.number);
if (isZero(arg2.value)) {
return createConstantNode(0);
}
// Ignore exponent; f(x)^1 = f(x)
if (expValue === '1') {
if (equal(arg2.value, 1)) {
return _derivative(arg1, constNodes);
}
}
@ -672,7 +674,7 @@ function factory (type, config, load, typed) {
arg1.clone(),
new OperatorNode('-', 'subtract', [
arg2,
new ConstantNode('1', config.number)
createConstantNode(1)
])
]);
@ -681,7 +683,7 @@ function factory (type, config, load, typed) {
new OperatorNode('*', 'multiply', [
_derivative(arg1, constNodes),
powMinusOne
]),
])
]);
}
@ -714,7 +716,7 @@ function factory (type, config, load, typed) {
*/
function funcArgsCheck(node) {
//TODO add min, max etc
if ((node.name == 'log' || node.name == 'nthRoot') && node.args.length == 2) {
if ((node.name === 'log' || node.name === 'nthRoot') && node.args.length === 2) {
return;
}
@ -723,13 +725,23 @@ function factory (type, config, load, typed) {
// Change all args to constants to avoid unidentified
// symbol error when compiling function
for (var i = 0; i < node.args.length; ++i) {
node.args[i] = new ConstantNode(0);
node.args[i] = createConstantNode(0);
}
node.compile().eval();
throw new Error('Expected TypeError, but none found');
}
/**
* Helper function to create a constant node with a specific type
* (number, BigNumber, Fraction)
* @param {number} value
* @param {string} [valueType]
* @return {ConstantNode}
*/
function createConstantNode(value, valueType) {
return new ConstantNode(numeric(value, valueType || config.number));
}
return derivative;
}

View File

@ -3,6 +3,7 @@
function factory (type, config, load, typed, math) {
var parse = load(require('../../expression/parse'));
var equal = load(require('../relational/equal'));
var ConstantNode = load(require('../../expression/node/ConstantNode'));
var FunctionNode = load(require('../../expression/node/FunctionNode'));
var OperatorNode = load(require('../../expression/node/OperatorNode'));
@ -599,7 +600,7 @@ function factory (type, config, load, typed, math) {
}
else if (rule instanceof ConstantNode) {
// Literal constant must match exactly
if(rule.value !== node.value) {
if(!equal(rule.value, node.value)) {
return [];
}
}
@ -624,7 +625,7 @@ function factory (type, config, load, typed, math) {
*/
function _exactMatch(p, q) {
if(p instanceof ConstantNode && q instanceof ConstantNode) {
if(p.value !== q.value) {
if(!equal(p.value, q.value)) {
return false;
}
}

View File

@ -4,6 +4,7 @@ var digits = require('./../../../utils/number').digits;
// TODO this could be improved by simplifying seperated constants under associative and commutative operators
function factory(type, config, load, typed, math) {
var util = load(require('./util'));
var isNumeric = load(require('../../utils/isNumeric'));
var isCommutative = util.isCommutative;
var isAssociative = util.isAssociative;
var allChildren = util.allChildren;
@ -151,7 +152,7 @@ function factory(type, config, load, typed, math) {
case 'SymbolNode':
return node;
case 'ConstantNode':
if (node.valueType === 'number') {
if (typeof node.value === 'number') {
return _toNumber(node.value);
}
return node;

View File

@ -1,6 +1,15 @@
'use strict';
function factory(type, config, load, typed, math) {
var equal = load(require('../../relational/equal'));
var isZero = load(require('../../utils/isZero'));
var isNumeric = load(require('../../utils/isNumeric'));
var add = load(require('../../arithmetic/add'));
var subtract = load(require('../../arithmetic/subtract'));
var multiply = load(require('../../arithmetic/multiply'));
var divide = load(require('../../arithmetic/divide'));
var pow = load(require('../../arithmetic/pow'));
var ConstantNode = math.expression.node.ConstantNode;
var OperatorNode = math.expression.node.OperatorNode;
var FunctionNode = math.expression.node.FunctionNode;
@ -41,13 +50,13 @@ function factory(type, config, load, typed, math) {
return node.args[0];
}
if (type.isConstantNode(a0)) {
if (a0.value === "0") {
if (isZero(a0.value)) {
return a1;
} else if (type.isConstantNode(a1) && a0.value && a0.value.length < 5 && a1.value && a1.value.length < 5) {
return new ConstantNode(Number(a0.value) + Number(a1.value));
} else if (type.isConstantNode(a1)) {
return new ConstantNode(add(a0.value, a1.value));
}
}
if (type.isConstantNode(a1) && a1.value === "0") {
if (type.isConstantNode(a1) && isZero(a1.value)) {
return a0;
}
if (node.args.length === 2 && type.isOperatorNode(a1) && a1.op === '-' && a1.fn === 'unaryMinus') {
@ -56,14 +65,14 @@ function factory(type, config, load, typed, math) {
return new OperatorNode(node.op, node.fn, a1 ? [a0,a1] : [a0]);
} else if (node.op === "-") {
if (type.isConstantNode(a0) && a1) {
if (type.isConstantNode(a1) && a0.value && a0.value.length < 5 && a1.value && a1.value.length < 5) {
return new ConstantNode(Number(a0.value) - Number(a1.value));
} else if (a0.value === "0") {
if (type.isConstantNode(a1)) {
return new ConstantNode(subtract(a0.value, a1.value));
} else if (isZero(a0.value)) {
return new OperatorNode("-", "unaryMinus", [a1]);
}
}
if (node.fn === "subtract" && node.args.length === 2) {
if (type.isConstantNode(a1) && a1.value === "0") {
if (type.isConstantNode(a1) && isZero(a1.value)) {
return a0;
}
if (type.isOperatorNode(a1) && a1.fn === "unaryMinus") {
@ -83,23 +92,23 @@ function factory(type, config, load, typed, math) {
throw new Error('never happens');
} else if (node.op === "*") {
if (type.isConstantNode(a0)) {
if (a0.value === "0") {
if (isZero(a0.value)) {
return node0;
} else if (a0.value === "1") {
} else if (equal(a0.value, 1)) {
return a1;
} else if (type.isConstantNode(a1) && a0.value && a0.value.length < 5 && a1.value && a1.value.length < 5) {
return new ConstantNode(Number(a0.value) * Number(a1.value));
} else if (type.isConstantNode(a1)) {
return new ConstantNode(multiply(a0.value, a1.value));
}
}
if (type.isConstantNode(a1)) {
if (a1.value === "0") {
if (isZero(a1.value)) {
return node0;
} else if (a1.value === "1") {
} else if (equal(a1.value, 1)) {
return a0;
} else if (type.isOperatorNode(a0) && a0.op === node.op) {
var a00 = a0.args[0];
if (type.isConstantNode(a00) && a1.value && a1.value.length < 5 && a00.value && a00.value.length < 5) {
var a00_a1 = new ConstantNode(Number(a0.args[0].value) * Number(a1.value));
if (type.isConstantNode(a00)) {
var a00_a1 = new ConstantNode(multiply(a00.value, a1.value));
return new OperatorNode(node.op, node.fn, [a00_a1, a0.args[1]]); // constants on left
}
}
@ -108,32 +117,30 @@ function factory(type, config, load, typed, math) {
return new OperatorNode(node.op, node.fn, [a0, a1]);
} else if (node.op === "/") {
if (type.isConstantNode(a0)) {
if (a0.value === "0") {
if (isZero(a0.value)) {
return node0;
} else if (type.isConstantNode(a1) && a0.value && a0.value.length < 5 && (a1.value === "1" || a1.value==="2" || a1.value==="4")) {
return new ConstantNode(Number(a0.value) / Number(a1.value));
} else if (type.isConstantNode(a1) &&
(equal(a1.value, 1) || equal(a1.value, 2) || equal(a1.value, 4))) {
return new ConstantNode(divide(a0.value, a1.value));
}
}
return new OperatorNode(node.op, node.fn, [a0, a1]);
} else if (node.op === "^") {
if (type.isConstantNode(a1)) {
if (a1.value === "0") {
if (isZero(a1.value)) {
return node1;
} else if (a1.value === "1") {
} else if (equal(a1.value, 1)) {
return a0;
} else {
if (type.isConstantNode(a0) &&
a0.value && a0.value.length < 5 &&
a1.value && a1.value.length < 2) {
if (type.isConstantNode(a0)) {
// fold constant
return new ConstantNode(
math.pow(Number(a0.value), Number(a1.value)));
return new ConstantNode(pow(a0.value, a1.value));
} else if (type.isOperatorNode(a0) && a0.op === "^") {
var a01 = a0.args[1];
if (type.isConstantNode(a01)) {
return new OperatorNode(node.op, node.fn, [
a0.args[0],
new ConstantNode(a01.value * a1.value)
new ConstantNode(multiply(a01.value, a1.value))
]);
}
}

View File

@ -1,7 +1,5 @@
'use strict';
var types = require('../../utils/types');
function factory (type, config, load, typed) {
/**
* Determine the type of a variable.
@ -48,11 +46,19 @@ function factory (type, config, load, typed) {
*/
var _typeof = typed('_typeof', {
'any': function (x) {
// JavaScript types
var t = types.type(x);
var t = typeof x;
// math.js types
if (t === 'Object') {
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';
if (x instanceof Boolean) return 'boolean';
if (x instanceof Number) return 'number';
if (x instanceof String) return 'string';
// math.js types
if (type.isBigNumber(x)) return 'BigNumber';
if (type.isComplex(x)) return 'Complex';
if (type.isFraction(x)) return 'Fraction';
@ -62,9 +68,13 @@ function factory (type, config, load, typed) {
if (type.isRange(x)) return 'Range';
if (type.isChain(x)) return 'Chain';
if (type.isHelp(x)) return 'Help';
return 'Object';
}
return t;
if (t === 'function') return 'Function';
return t; // can be 'string', 'number', 'boolean', ...
}
});

50
lib/type/numeric.js Normal file
View File

@ -0,0 +1,50 @@
'use strict';
function factory(type, config, load, typed) {
// TODO: expose this function to mathjs, add documentation
/**
* Create a numeric value with a specific type: number, BigNumber, or Fraction
*
* @param {string | number} value
* @param {'number' | 'BigNumber' | 'Fraction'}
* @return {number | BigNumber | Fraction} Returns an instance of the
* numeric requested type
*/
return function numeric (value, valueType) {
if (valueType === 'BigNumber') {
return new type.BigNumber(value);
}
else if (valueType === 'Fraction') {
return new type.Fraction(value);
}
else {
// valueType === 'number' or undefined // TODO: check this
if (typeof value === 'number') {
return value;
}
else {
if (value === 'Infinity') {
return Infinity;
}
if (value === 'NaN') {
return NaN;
}
// The following regexp is relatively permissive
if (!/^[\-+]?((\d+\.?\d*)|(\d*\.?\d+))([eE][+\-]?\d+)?$/.test(value)) {
throw new Error('Invalid numeric value "' + value + '"');
}
// remove leading zeros like '003.2' which are not allowed by JavaScript
return parseFloat(value.replace(/^(0*)[0-9]/, function (match, zeros) {
return match.substring(zeros.length);
}));
}
}
}
}
exports.factory = factory;

View File

@ -2,8 +2,6 @@
var number = require('./number');
var string = require('./string');
var object = require('./object');
var types = require('./types');
var DimensionError = require('../error/DimensionError');
var IndexError = require('../error/IndexError');

View File

@ -6,5 +6,4 @@ exports['function'] = require('./function');
exports.number = require('./number');
exports.object = require('./object');
exports.string = require('./string');
exports.types = require('./types');
exports.emitter = require('./emitter');

View File

@ -1,44 +0,0 @@
'use strict';
/**
* Determine the type of a variable
*
* type(x)
*
* The following types are recognized:
*
* 'undefined'
* 'null'
* 'boolean'
* 'number'
* 'string'
* 'Array'
* 'Function'
* 'Date'
* 'RegExp'
* 'Object'
*
* @param {*} x
* @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'.
*/
exports.type = function(x) {
var type = typeof x;
if (type === 'object') {
if (x === null) return 'null';
if (Array.isArray(x)) return 'Array';
if (x instanceof Date) return 'Date';
if (x instanceof RegExp) return 'RegExp';
if (x instanceof Boolean) return 'boolean';
if (x instanceof Number) return 'number';
if (x instanceof String) return 'string';
return 'Object';
}
if (type === 'function') return 'Function';
return type;
};

View File

@ -411,7 +411,7 @@ describe('AccessorNode', function() {
return string;
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -450,7 +450,7 @@ describe('AccessorNode', function() {
return latex;
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -266,7 +266,7 @@ describe('ArrayNode', function() {
return string;
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -303,7 +303,7 @@ describe('ArrayNode', function() {
return latex;
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -153,7 +153,7 @@ describe('AssignmentNode', function() {
new bigmath.expression.node.ConstantNode(2),
new bigmath.expression.node.ConstantNode(1)
]);
var value = new bigmath.expression.node.ConstantNode(5);
var value = new bigmath.expression.node.ConstantNode(bigmath.bignumber(5));
var n = new bigmath.expression.node.AssignmentNode(object, index, value);
var expr = n.compile();
@ -190,8 +190,8 @@ describe('AssignmentNode', function() {
assert.deepEqual(n.filter(function (node) {return node.isAssignmentNode}), [n]);
assert.deepEqual(n.filter(function (node) {return node.isSymbolNode}), [a]);
assert.deepEqual(n.filter(function (node) {return node.isConstantNode}), [b, c, v]);
assert.deepEqual(n.filter(function (node) {return node.value === '1'}), [c]);
assert.deepEqual(n.filter(function (node) {return node.value === '2'}), [b, v]);
assert.deepEqual(n.filter(function (node) {return node.value === 1}), [c]);
assert.deepEqual(n.filter(function (node) {return node.value === 2}), [b, v]);
assert.deepEqual(n.filter(function (node) {return node.name === 'q'}), []);
});
@ -203,7 +203,7 @@ describe('AssignmentNode', function() {
assert.deepEqual(n.filter(function (node) {return node.isAssignmentNode}), [n]);
assert.deepEqual(n.filter(function (node) {return node.isSymbolNode}), [a]);
assert.deepEqual(n.filter(function (node) {return node.isConstantNode}), [v]);
assert.deepEqual(n.filter(function (node) {return node.value === '2'}), [v]);
assert.deepEqual(n.filter(function (node) {return node.value === 2}), [v]);
assert.deepEqual(n.filter(function (node) {return node.name === 'q'}), []);
});
@ -498,7 +498,7 @@ describe('AssignmentNode', function() {
' equals ' + node.value.toString(options);
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -533,7 +533,7 @@ describe('AssignmentNode', function() {
'\\mbox{equals}' + node.value.toTex(options);
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -281,7 +281,7 @@ describe('BlockNode', function() {
return string;
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -315,7 +315,7 @@ describe('BlockNode', function() {
return latex;
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -288,7 +288,7 @@ describe('ConditionalNode', function() {
+ ' else ' + node.falseExpr.toString(options);
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -317,7 +317,7 @@ describe('ConditionalNode', function() {
+ ' else ' + node.falseExpr.toTex(options);
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -6,27 +6,22 @@ var bigmath = require('../../../index').create({number: 'BigNumber'});
var Node = math.expression.node.Node;
var ConstantNode = math.expression.node.ConstantNode;
var SymbolNode = math.expression.node.SymbolNode;
var Fraction = require('../../../lib/type/fraction/Fraction');
describe('ConstantNode', function() {
it ('should create a ConstantNode with value type', function () {
var a = new ConstantNode('3', 'number');
assert(a instanceof Node);
assert.equal(a.type, 'ConstantNode');
});
it ('should create a ConstantNode without value type', function () {
it ('should create a ConstantNode', function () {
var a = new ConstantNode(3);
assert(a instanceof Node);
assert.equal(a.type, 'ConstantNode');
// TODO: extensively test each of the supported types
assert.deepEqual(new ConstantNode(3), new ConstantNode('3', 'number'));
assert.deepEqual(new ConstantNode('hello'), new ConstantNode('hello', 'string'));
assert.deepEqual(new ConstantNode(true), new ConstantNode('true', 'boolean'));
assert.deepEqual(new ConstantNode(false), new ConstantNode('false', 'boolean'));
assert.deepEqual(new ConstantNode(null), new ConstantNode('null', 'null'));
assert.deepEqual(new ConstantNode(undefined), new ConstantNode('undefined', 'undefined'));
assert.strictEqual(new ConstantNode(3).value, 3);
assert.strictEqual(new ConstantNode('hello').value, 'hello');
assert.strictEqual(new ConstantNode(true).value, true);
assert.strictEqual(new ConstantNode(false).value, false);
assert.strictEqual(new ConstantNode(null).value, null);
assert.strictEqual(new ConstantNode(undefined).value, undefined);
});
it ('should have isConstantNode', function () {
@ -35,68 +30,50 @@ describe('ConstantNode', function() {
});
it ('should throw an error when calling without new operator', function () {
assert.throws(function () {ConstantNode('3', 'number')}, SyntaxError);
});
it ('should throw an error in case of wrong construction arguments', function () {
assert.throws(function () {new ConstantNode(3, 'number');}, TypeError);
assert.throws(function () {new ConstantNode(new Date());}, TypeError);
assert.throws(function () {new ConstantNode('3', Number);}, TypeError);
});
it ('should throw an error in case of unknown type of constant', function () {
assert.throws(function () {new ConstantNode('3', 'bla').compile();}, TypeError);
assert.throws(function () {ConstantNode(3)}, SyntaxError);
});
it ('should compile a ConstantNode', function () {
var expr = new ConstantNode('2.3', 'number').compile();
var expr = new ConstantNode(2.3).compile();
assert.strictEqual(expr.eval(), 2.3);
expr = new ConstantNode('002.3', 'number').compile();
expr = new ConstantNode(2.3).compile();
assert.strictEqual(expr.eval(), 2.3);
expr = new ConstantNode('hello', 'string').compile();
expr = new ConstantNode('hello').compile();
assert.strictEqual(expr.eval(), 'hello');
expr = new ConstantNode('true', 'boolean').compile();
expr = new ConstantNode(true).compile();
assert.strictEqual(expr.eval(), true);
expr = new ConstantNode('undefined', 'undefined').compile();
expr = new ConstantNode(undefined).compile();
assert.strictEqual(expr.eval(), undefined);
expr = new ConstantNode('null', 'null').compile();
expr = new ConstantNode(null).compile();
assert.strictEqual(expr.eval(), null);
});
it ('should compile a ConstantNode with bigmath', function () {
var expr = new bigmath.expression.node.ConstantNode('2.3', 'number').compile();
var constantNode = bigmath.parse('2.3');
assert.ok(constantNode.isConstantNode);
var expr = constantNode.compile();
assert.deepEqual(expr.eval(), new bigmath.type.BigNumber(2.3));
});
it ('should find a ConstantNode', function () {
var a = new ConstantNode('2', 'number');
var a = new ConstantNode(2);
assert.deepEqual(a.filter(function (node) {return node instanceof ConstantNode}), [a]);
assert.deepEqual(a.filter(function (node) {return node instanceof SymbolNode}), []);
});
it ('should throw an error when compiling an invalid value', function () {
var clone = math.create();
clone.config({number: 'number'});
assert.throws(function () { new ConstantNode('console.log("foo")', 'number').compile() }, /Invalid numeric value/)
clone.config({number: 'BigNumber'});
assert.throws(function () { new ConstantNode('console.log("foo")', 'number').compile() }, /Invalid numeric value/)
clone.config({number: 'Fraction'});
assert.throws(function () { new ConstantNode('console.log("foo")', 'number').compile() }, /Invalid numeric value/)
});
it ('should leave quotes in strings as is (no escaping)', function () {
assert.strictEqual( new ConstantNode('"+foo+"', 'string').compile().eval(), '"+foo+"')
assert.strictEqual( new ConstantNode('\\"escaped\\"', 'string').compile().eval(), '\\"escaped\\"')
assert.strictEqual( new ConstantNode('"+foo+"').compile().eval(), '"+foo+"')
assert.strictEqual( new ConstantNode('\\"escaped\\"').compile().eval(), '\\"escaped\\"')
});
it ('should find a ConstantNode', function () {
var a = new ConstantNode('2', 'number');
var a = new ConstantNode(2);
assert.deepEqual(a.filter(function (node) {return node instanceof ConstantNode}), [a]);
assert.deepEqual(a.filter(function (node) {return node instanceof SymbolNode}), []);
});
@ -152,61 +129,71 @@ describe('ConstantNode', function() {
assert.strictEqual(a.equals(undefined), false);
assert.strictEqual(a.equals(new ConstantNode(2)), true);
assert.strictEqual(a.equals(new ConstantNode(3)), false);
assert.strictEqual(a.equals(new ConstantNode('2', 'number')), true);
assert.strictEqual(a.equals(new ConstantNode('2', 'string')), false);
assert.strictEqual(a.equals(new ConstantNode('2')), false);
assert.strictEqual(a.equals(new SymbolNode('2')), false);
assert.strictEqual(a.equals({value:2, valueType: 'number'}), false);
assert.strictEqual(a.equals({value:2}), false);
});
it ('should stringify a ConstantNode', function () {
assert.equal(new ConstantNode('3', 'number').toString(), '3');
assert.deepEqual(new ConstantNode('3', 'number').toString(), '3');
assert.equal(new ConstantNode('hi', 'string').toString(), '"hi"');
assert.equal(new ConstantNode('true', 'boolean').toString(), 'true');
assert.equal(new ConstantNode('false', 'boolean').toString(), 'false');
assert.equal(new ConstantNode('undefined', 'undefined').toString(), 'undefined');
assert.equal(new ConstantNode('null', 'null').toString(), 'null');
assert.equal(new ConstantNode(3).toString(), '3');
assert.deepEqual(new ConstantNode(3).toString(), '3');
assert.deepEqual(new ConstantNode(math.bignumber('1e500')).toString(), '1e+500');
assert.deepEqual(new ConstantNode(math.fraction(2,3)).toString(), '2/3');
assert.equal(new ConstantNode('hi').toString(), '"hi"');
assert.equal(new ConstantNode(true).toString(), 'true');
assert.equal(new ConstantNode(false).toString(), 'false');
assert.equal(new ConstantNode(undefined).toString(), 'undefined');
assert.equal(new ConstantNode(null).toString(), 'null');
});
it ('should stringify a ConstantNode with custom toString', function () {
//Also checks if the custom functions get passed on to the children
var customFunction = function (node, options) {
if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ')'
}
};
var n = new ConstantNode(1);
assert.equal(n.toString({handler: customFunction}), 'const(1, number)');
assert.equal(n.toString({handler: customFunction}), 'const(1)');
});
it ('should LaTeX a ConstantNode', function () {
assert.equal(new ConstantNode('3', 'number').toTex(), '3');
assert.deepEqual(new ConstantNode('3', 'number').toTex(), '3');
assert.equal(new ConstantNode('hi', 'string').toTex(), '\\mathtt{"hi"}');
assert.equal(new ConstantNode('true', 'boolean').toTex(), 'true');
assert.equal(new ConstantNode('false', 'boolean').toTex(), 'false');
assert.equal(new ConstantNode('undefined', 'undefined').toTex(), 'undefined');
assert.equal(new ConstantNode('null', 'null').toTex(), 'null');
assert.equal(new ConstantNode(3).toTex(), '3');
assert.deepEqual(new ConstantNode(3).toTex(), '3');
assert.deepEqual(new ConstantNode(math.bignumber('3')).toTex(), '3');
assert.equal(new ConstantNode('hi').toTex(), '\\mathtt{"hi"}');
assert.equal(new ConstantNode(true).toTex(), 'true');
assert.equal(new ConstantNode(false).toTex(), 'false');
assert.equal(new ConstantNode(undefined).toTex(), 'undefined');
assert.equal(new ConstantNode(null).toTex(), 'null');
});
it ('should LaTeX a ConstantNode in exponential notation', function () {
var n = new ConstantNode('1e10', 'number');
assert.equal(n.toTex(), '1\\cdot10^{10}');
var n = new ConstantNode(1e10);
assert.equal(n.toTex(), '1\\cdot10^{+10}');
});
it ('should LaTeX a ConstantNode with custom toTex', function () {
//Also checks if the custom functions get passed on to the children
var customFunction = function (node, options) {
if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + '\\right)'
}
};
var n = new ConstantNode(1);
assert.equal(n.toTex({handler: customFunction}), 'const\\left(1, number\\right)');
assert.equal(n.toTex({handler: customFunction}), 'const\\left(1\\right)');
});
it ('should LaTeX a ConstantNode with a fraction', function () {
var positive = new ConstantNode(new math.type.Fraction(1.5));
var negative = new ConstantNode(new math.type.Fraction(-1.5));
assert.equal(positive.toTex(), '\\frac{3}{2}');
assert.equal(negative.toTex(), '-\\frac{3}{2}');
});
});

View File

@ -381,7 +381,7 @@ describe('FunctionAssignmentNode', function() {
return string;
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -424,7 +424,7 @@ describe('FunctionAssignmentNode', function() {
return latex;
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -410,7 +410,7 @@ describe('FunctionNode', function() {
return string;
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -478,7 +478,7 @@ describe('FunctionNode', function() {
return latex;
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -85,7 +85,7 @@ describe('IndexNode', function() {
paths.push(path);
assert.strictEqual(parent, n);
return node.isConstantNode && node.value === '1' ? e : node;
return node.isConstantNode && node.value === 1 ? e : node;
});
assert.equal(nodes.length, 2);
@ -115,7 +115,7 @@ describe('IndexNode', function() {
var e = new SymbolNode('c');
var f = n.transform(function (node) {
return node.isConstantNode && node.value === '1' ? e : node;
return node.isConstantNode && node.value === 1 ? e : node;
});
assert.notStrictEqual(f, n);
@ -197,7 +197,7 @@ describe('IndexNode', function() {
}).join(', ');
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -240,7 +240,7 @@ describe('IndexNode', function() {
return latex;
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -247,7 +247,7 @@ describe('ObjectNode', function() {
it ('should stringify an ObjectNode with custom toString', function () {
var customFunction = function (node, options) {
if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -271,7 +271,7 @@ describe('ObjectNode', function() {
it ('should LaTeX an ObjectNode with custom toTex', function () {
var customFunction = function (node, options) {
if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -367,7 +367,7 @@ describe('OperatorNode', function() {
+ ', ' + node.args[1].toString(options) + ')';
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -390,7 +390,7 @@ describe('OperatorNode', function() {
node.args[1].toString(options);
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -591,7 +591,7 @@ describe('OperatorNode', function() {
+ ', ' + node.args[1].toTex(options) + ')';
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};
@ -614,7 +614,7 @@ describe('OperatorNode', function() {
node.args[1].toTex(options);
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -301,7 +301,7 @@ describe('RangeNode', function() {
+ ' with steps of ' + node.step.toString(options);
}
else if (node.type === 'ConstantNode') {
return 'const(' + node.value + ', ' + node.valueType + ')'
return 'const(' + node.value + ', ' + math.typeof(node.value) + ')'
}
};
@ -345,7 +345,7 @@ describe('RangeNode', function() {
+ ' with steps of ' + node.step.toTex(options);
}
else if (node.type === 'ConstantNode') {
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
return 'const\\left(' + node.value + ', ' + math.typeof(node.value) + '\\right)'
}
};

View File

@ -5,6 +5,7 @@ var math = require('../../index');
var ArgumentsError = require('../../lib/error/ArgumentsError');
var parse = math.expression.parse;
var ConditionalNode = math.expression.node.ConditionalNode;
var ConstantNode = math.expression.node.ConstantNode;
var OperatorNode = math.expression.node.OperatorNode;
var RangeNode = math.expression.node.RangeNode;
var Complex = math.type.Complex;
@ -368,10 +369,6 @@ describe('parse', function() {
assert.ok(parseAndEval('5cm') instanceof Unit);
});
it('should parse constants', function() {
assert.equal(parseAndEval('pi'), Math.PI);
});
it('should parse physical constants', function() {
var expected = new Unit(299792458, 'm/s');
expected.fixPrefix = true;
@ -844,12 +841,35 @@ describe('parse', function() {
describe('constants', function () {
it('should parse constants', function() {
it ('should parse symbolic constants', function () {
assert.strictEqual(parse('i').type, 'SymbolNode');
assert.deepEqual(parseAndEval('i'), new Complex(0, 1));
approx.equal(parseAndEval('pi'), Math.PI);
approx.equal(parseAndEval('e'), Math.E);
})
it('should parse constants', function() {
assert.strictEqual(parse('true').type, 'ConstantNode');
assert.deepStrictEqual(parse('true'), createConstantNode(true));
assert.deepStrictEqual(parse('false'), createConstantNode(false));
assert.deepStrictEqual(parse('null'), createConstantNode(null));
assert.deepStrictEqual(parse('undefined'), createConstantNode(undefined));
assert.deepStrictEqual(parse('uninitialized'), createConstantNode(math.uninitialized));
});
it('should parse numeric constants', function() {
var nanConstantNode = parse('NaN');
assert.deepStrictEqual(nanConstantNode.type, 'ConstantNode');
assert.ok(isNaN(nanConstantNode.value));
assert.deepStrictEqual(parse('Infinity'), createConstantNode(Infinity));
});
// helper function to create a ConstantNode with empty comment
function createConstantNode (value) {
var c = new ConstantNode(value);
c.comment = ''
return c;
}
});
describe('variables', function () {

View File

@ -192,7 +192,7 @@ describe('security', function () {
it ('should not allow calling eval via clone', function () {
assert.throws(function () {
math.eval('expression.node.ConstantNode.prototype.clone.call({"value":"eval", "valueType":"null"}).eval()("console.log(\'hacked...\')")')
math.eval('expression.node.ConstantNode.prototype.clone.call({"value":"eval"}).eval()("console.log(\'hacked...\')")')
}, /Error: Undefined symbol expression/);
})

View File

@ -260,15 +260,16 @@ describe('simplify', function() {
assert.equal(math.simplify('LN10', ['LN10 -> 1']).toString(), '1');
assert.equal(math.simplify('LOG2E', ['LOG2E -> 1']).toString(), '1');
assert.equal(math.simplify('LOG10E', ['LOG10E -> 1']).toString(), '1');
assert.equal(math.simplify('NaN', ['NaN -> 1']).toString(), '1');
assert.equal(math.simplify('null', ['null -> 1']).toString(), '1');
assert.equal(math.simplify('phi', ['phi -> 1']).toString(), '1');
assert.equal(math.simplify('SQRT1_2', ['SQRT1_2 -> 1']).toString(), '1');
assert.equal(math.simplify('SQRT2', ['SQRT2 -> 1']).toString(), '1');
assert.equal(math.simplify('tau', ['tau -> 1']).toString(), '1');
// note that NaN is a special case, we can't compare two values both NaN.
});
it('should throw an error for invalid built-in constant symbols in rules', function() {
assert.throws(function(){ math.simplify('null', ['null -> 1']).toString(); });
assert.throws(function(){ math.simplify('uninitialized', ['uninitialized -> 1']).toString(); });
assert.throws(function(){ math.simplify('version', ['version -> 1']).toString(); });
});

View File

@ -14,6 +14,8 @@ describe('typeof', function() {
it('should return number type for a number', function() {
assert.equal(math.typeof(2), 'number');
assert.equal(math.typeof(new Number(2)), 'number');
assert.equal(math.typeof(new Number(2.3)), 'number');
assert.equal(math.typeof(NaN), 'number');
});
it('should return bignumber type for a bignumber', function() {
@ -66,7 +68,7 @@ describe('typeof', function() {
assert.equal(math.typeof(null), 'null');
});
it('should return undefined type for undefined', function() {
it('should return undefined type for undefined', function() {
assert.equal(math.typeof(undefined), 'undefined');
});
@ -74,6 +76,10 @@ describe('typeof', function() {
assert.equal(math.typeof(new Date()), 'Date');
});
it('should return the type of a regexp', function () {
assert.equal(math.typeof(/regexp/), 'RegExp');
});
it('should return function type for a function', function() {
assert.equal(math.typeof(function () {}), 'Function');
assert.equal(math.typeof(new Function ()), 'Function');
@ -110,4 +116,9 @@ describe('typeof', function() {
assert.equal(expression.toTex(), '\\mathrm{typeof}\\left(1\\right)');
});
it('should throw an error in case of wrong number of arguments', function () {
assert.throws(function () {math.typeof()}, /Too few arguments in function _typeof/);
assert.throws(function () {math.typeof(1,2,3)}, /Too many arguments in function _typeof/);
})
});

View File

@ -1,53 +0,0 @@
// test types utils
var assert = require('assert'),
approx = require('../../tools/approx'),
types = require('../../lib/utils/types');
describe ('types', function () {
it('should return the type of undefined', function () {
assert.equal(types.type(undefined), 'undefined');
assert.equal(types.type(), 'undefined');
});
it('should return the type of a boolean', function () {
assert.equal(types.type(false), 'boolean');
assert.equal(types.type(true), 'boolean');
});
it('should return the type of a number', function () {
assert.equal(types.type(2.3), 'number');
assert.equal(types.type(Number(2.3)), 'number');
assert.equal(types.type(new Number(2.3)), 'number');
assert.equal(types.type(NaN), 'number');
});
it('should return the type of a string', function () {
assert.equal(types.type('bla'), 'string');
assert.equal(types.type(new String('bla')), 'string');
});
it('should return the type of an object', function () {
assert.equal(types.type({}), 'Object');
assert.equal(types.type(new Object()), 'Object');
});
it('should return the type of an array', function () {
assert.equal(types.type([]), 'Array');
assert.equal(types.type(new Array()), 'Array');
});
it('should return the type of a function', function () {
assert.equal(types.type(function () {}), 'Function');
});
it('should return the type of a date', function () {
assert.equal(types.type(new Date()), 'Date');
});
it('should return the type of a regexp', function () {
assert.equal(types.type(/regexp/), 'RegExp');
});
});