diff --git a/HISTORY.md b/HISTORY.md
index 5aecd58d5..14ece3a3a 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -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.
diff --git a/docs/expressions/expression_trees.md b/docs/expressions/expression_trees.md
index 5241e2def..0b80b09b7 100644
--- a/docs/expressions/expression_trees.md
+++ b/docs/expressions/expression_trees.md
@@ -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');
```
diff --git a/lib/expression/node/ConstantNode.js b/lib/expression/node/ConstantNode.js
index 9337479cb..a4c6e2920 100644
--- a/lib/expression/node/ConstantNode.js
+++ b/lib/expression/node/ConstantNode.js
@@ -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 '' + value + '';
case 'string':
return '' + value + '';
@@ -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;
}
diff --git a/lib/expression/node/IndexNode.js b/lib/expression/node/IndexNode.js
index 39026d41c..25376a43c 100644
--- a/lib/expression/node/IndexNode.js
+++ b/lib/expression/node/IndexNode.js
@@ -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';
};
/**
diff --git a/lib/expression/parse.js b/lib/expression/parse.js
index 41d64f434..3e777c21a 100644
--- a/lib/expression/parse.js
+++ b/lib/expression/parse.js
@@ -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();
diff --git a/lib/function/algebra/derivative.js b/lib/function/algebra/derivative.js
index fdc61b1ba..bcb7c2476 100644
--- a/lib/function/algebra/derivative.js
+++ b/lib/function/algebra/derivative.js
@@ -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;
}
diff --git a/lib/function/algebra/simplify.js b/lib/function/algebra/simplify.js
index 9ad5df329..ae4f3a343 100644
--- a/lib/function/algebra/simplify.js
+++ b/lib/function/algebra/simplify.js
@@ -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;
}
}
diff --git a/lib/function/algebra/simplify/simplifyConstant.js b/lib/function/algebra/simplify/simplifyConstant.js
index 8b22ab15e..8950e26bf 100644
--- a/lib/function/algebra/simplify/simplifyConstant.js
+++ b/lib/function/algebra/simplify/simplifyConstant.js
@@ -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;
diff --git a/lib/function/algebra/simplify/simplifyCore.js b/lib/function/algebra/simplify/simplifyCore.js
index f5e1cd5e7..a1ca57424 100644
--- a/lib/function/algebra/simplify/simplifyCore.js
+++ b/lib/function/algebra/simplify/simplifyCore.js
@@ -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))
]);
}
}
diff --git a/lib/function/utils/typeof.js b/lib/function/utils/typeof.js
index ef9b30550..fde4b4e84 100644
--- a/lib/function/utils/typeof.js
+++ b/lib/function/utils/typeof.js
@@ -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', ...
}
});
diff --git a/lib/type/numeric.js b/lib/type/numeric.js
new file mode 100644
index 000000000..65e44fd10
--- /dev/null
+++ b/lib/type/numeric.js
@@ -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;
diff --git a/lib/utils/array.js b/lib/utils/array.js
index 8c9171290..ac527b78d 100644
--- a/lib/utils/array.js
+++ b/lib/utils/array.js
@@ -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');
diff --git a/lib/utils/index.js b/lib/utils/index.js
index a28d1565a..266681eb4 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -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');
diff --git a/lib/utils/types.js b/lib/utils/types.js
deleted file mode 100644
index 2154fd62b..000000000
--- a/lib/utils/types.js
+++ /dev/null
@@ -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;
-};
diff --git a/test/expression/node/AccessorNode.test.js b/test/expression/node/AccessorNode.test.js
index 156913cf9..3778c4544 100644
--- a/test/expression/node/AccessorNode.test.js
+++ b/test/expression/node/AccessorNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/ArrayNode.test.js b/test/expression/node/ArrayNode.test.js
index 5702ccc2d..7f9c31b29 100644
--- a/test/expression/node/ArrayNode.test.js
+++ b/test/expression/node/ArrayNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/AssignmentNode.test.js b/test/expression/node/AssignmentNode.test.js
index 74637e660..5bab826ae 100644
--- a/test/expression/node/AssignmentNode.test.js
+++ b/test/expression/node/AssignmentNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/BlockNode.test.js b/test/expression/node/BlockNode.test.js
index 21ec0c773..2797ef196 100644
--- a/test/expression/node/BlockNode.test.js
+++ b/test/expression/node/BlockNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/ConditionalNode.test.js b/test/expression/node/ConditionalNode.test.js
index 63468bb13..9a47dac81 100644
--- a/test/expression/node/ConditionalNode.test.js
+++ b/test/expression/node/ConditionalNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/ConstantNode.test.js b/test/expression/node/ConstantNode.test.js
index 4bdaf21d0..71dbb8ebb 100644
--- a/test/expression/node/ConstantNode.test.js
+++ b/test/expression/node/ConstantNode.test.js
@@ -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}');
});
});
diff --git a/test/expression/node/FunctionAssignmentNode.test.js b/test/expression/node/FunctionAssignmentNode.test.js
index 28e391fb5..29c1f7fe4 100644
--- a/test/expression/node/FunctionAssignmentNode.test.js
+++ b/test/expression/node/FunctionAssignmentNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/FunctionNode.test.js b/test/expression/node/FunctionNode.test.js
index 0339a2447..643e2d897 100644
--- a/test/expression/node/FunctionNode.test.js
+++ b/test/expression/node/FunctionNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/IndexNode.test.js b/test/expression/node/IndexNode.test.js
index 4f8a9020c..f369b82d4 100644
--- a/test/expression/node/IndexNode.test.js
+++ b/test/expression/node/IndexNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/ObjectNode.test.js b/test/expression/node/ObjectNode.test.js
index 628f525a7..544255385 100644
--- a/test/expression/node/ObjectNode.test.js
+++ b/test/expression/node/ObjectNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/OperatorNode.test.js b/test/expression/node/OperatorNode.test.js
index 67d2b7b65..549e96b1f 100644
--- a/test/expression/node/OperatorNode.test.js
+++ b/test/expression/node/OperatorNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/node/RangeNode.test.js b/test/expression/node/RangeNode.test.js
index 4d1d17108..642e57b6c 100644
--- a/test/expression/node/RangeNode.test.js
+++ b/test/expression/node/RangeNode.test.js
@@ -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)'
}
};
diff --git a/test/expression/parse.test.js b/test/expression/parse.test.js
index bff295e76..863910202 100644
--- a/test/expression/parse.test.js
+++ b/test/expression/parse.test.js
@@ -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 () {
diff --git a/test/expression/security.test.js b/test/expression/security.test.js
index b18651c81..ef04abf58 100644
--- a/test/expression/security.test.js
+++ b/test/expression/security.test.js
@@ -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/);
})
diff --git a/test/function/algebra/simplify.test.js b/test/function/algebra/simplify.test.js
index 27d5700bf..b89ac2e04 100644
--- a/test/function/algebra/simplify.test.js
+++ b/test/function/algebra/simplify.test.js
@@ -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(); });
});
diff --git a/test/function/utils/typeof.test.js b/test/function/utils/typeof.test.js
index ea46fa29b..55b551376 100644
--- a/test/function/utils/typeof.test.js
+++ b/test/function/utils/typeof.test.js
@@ -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/);
+ })
+
});
diff --git a/test/utils/types.test.js b/test/utils/types.test.js
deleted file mode 100644
index c826c6636..000000000
--- a/test/utils/types.test.js
+++ /dev/null
@@ -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');
- });
-
-});
\ No newline at end of file