Socket
Socket
Sign inDemoInstall

@mathigon/hilbert

Package Overview
Dependencies
Maintainers
1
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mathigon/hilbert - npm Package Compare versions

Comparing version 0.6.2 to 0.6.3

.github/workflows/codeql.yml

266

dist/hilbert.cjs.js

@@ -167,7 +167,8 @@ 'use strict';

}
const SPECIAL = new Set(['sin', 'cos', 'tan', 'sec', 'csc', 'cot', 'arcsin',
'arccos', 'arctan', 'sinh', 'cosh', 'tanh', 'sech', 'csch', 'coth', 'exp',
'log', 'ln', 'det', 'dim', 'mod', 'gcd', 'lcm', 'min', 'max']);
const SPECIAL_FUNCTIONS = ['abs', 'round', 'floor', 'ceil', 'max', 'min', 'mod',
'lcm', 'gcd', 'gcf', 'log', 'exp', 'ln', 'sqrt', 'root', 'sin', 'cos', 'tan',
'sec', 'csc', 'cot', 'cosec', 'cotan', 'arcsin', 'arccos', 'arctan', 'sinh',
'cosh', 'tanh', 'sech', 'csch', 'coth', 'cosech'];
function isSpecialFunction(fn) {
return SPECIAL.has(fn);
return SPECIAL_FUNCTIONS.includes(fn);
}

@@ -208,2 +209,5 @@ const VOICE_STRINGS = {

}
interval(vars = {}) {
return [this.evaluate(vars), this.evaluate(vars)];
}
/** Substitutes a new expression for a variable. */

@@ -273,2 +277,11 @@ substitute(_vars = {}) {

}
interval(vars = {}) {
var _a;
const x = (_a = vars[this.i]) !== null && _a !== void 0 ? _a : CONSTANTS[this.i];
if (Array.isArray(x))
return x;
if (typeof x === 'number')
return [x, x];
throw ExprError.undefinedVariable(this.i);
}
toMathML() {

@@ -345,2 +358,178 @@ const variant = isSpecialFunction(this.i) ? ' mathvariant="normal"' : '';

// =============================================================================
const WHOLE = [-Infinity, Infinity];
const EMPTY = [NaN, NaN];
const HALF_PI = Math.PI / 2;
const TWO_PI = Math.PI * 2;
const int = (a, b) => [a - Number.EPSILON, b + Number.EPSILON];
const range = (...args) => int(Math.min(...args), Math.max(...args));
const width = (a) => Math.abs(a[1] - a[0]);
const isEmpty = (a) => isNaN(a[0]) || isNaN(a[1]);
const isInfinite = (a) => !isFinite(a[0]) && a[0] === a[1];
const contains = (a, v) => fermat.isBetween(v, a[0] - Number.EPSILON, a[1] + Number.EPSILON);
const hasZero = (a) => contains(a, 0);
// -----------------------------------------------------------------------------
// Standard Evaluation
const evaluate = {
add: (...args) => args.reduce((a, b) => a + b, 0),
sub: (...args) => (args.length > 1) ? args[0] - args[1] : -args[0],
mul: (...args) => args.reduce((a, b) => a * b, 1),
div: (a, b) => a / b,
abs: (a) => Math.abs(a),
round: (a) => Math.round(a),
floor: (a) => Math.floor(a),
ceil: (a) => Math.ceil(a),
max: (...args) => Math.max(...args),
min: (...args) => Math.min(...args),
mod: (a, b) => a % b,
lcm: (...args) => fermat.lcm(...args),
gcd: (...args) => fermat.gcd(...args),
gcf: (...args) => fermat.gcd(...args),
sup: (a, b) => Math.pow(a, b),
log: (a, b) => Math.log(a) / (b === undefined ? 1 : Math.log(b)),
exp: (a) => Math.exp(a),
ln: (a) => Math.log(a),
sqrt: (a) => Math.sqrt(a),
root: (a, b) => Math.pow(a, 1 / b),
sin: (a) => Math.sin(a),
cos: (a) => Math.cos(a),
tan: (a) => Math.tan(a),
sec: (a) => 1 / Math.cos(a),
csc: (a) => 1 / Math.sin(a),
cot: (a) => 1 / Math.tan(a),
cosec: (a) => evaluate.csc(a),
cotan: (a) => evaluate.cot(a),
arcsin: (a) => Math.asin(a),
arccos: (a) => Math.acos(a),
arctan: (a) => Math.atan(a),
sinh: (a) => Math.sinh(a),
cosh: (a) => Math.cosh(a),
tanh: (a) => Math.tanh(a),
sech: (a) => 1 / Math.cosh(a),
csch: (a) => 1 / Math.sinh(a),
coth: (a) => 1 / Math.tanh(a),
cosech: (a) => evaluate.csch(a),
};
// -----------------------------------------------------------------------------
// Utility Functions
/** Evaluate a^b */
function pow(a, b) {
// If the base a is positive:
if (a[0] > 0) {
if (b[0] >= 0)
return int(a[0] ** b[0], a[1] ** b[1]);
return range(a[0] ** b[0], a[0] ** b[1], a[1] ** b[0], a[1] ** b[1]);
}
// If the exponent b is an integer:
const k = b[0];
if (Number.isInteger(k) && k === b[1]) {
if (k === 0)
return [hasZero(a) ? 0 : 1, 1];
if (k % 2)
return int(a[0] ** k, a[1] ** k);
if (hasZero(a))
return [0, Math.max(a[0] ** k, a[1] ** k)];
return [a[1] ** k, a[0] ** k];
}
return EMPTY; // TODO Implement this!
}
/** Shift an interval so that a[0] lies between 0 and 2 Pi. */
function intervalMod(a, m = TWO_PI) {
const d = Math.floor(a[0] / m) * m;
return [a[0] - d, a[1] - d];
}
// -----------------------------------------------------------------------------
// Interval Evaluation
const interval = {
add: (a, b) => int(a[0] + b[0], a[1] + b[1]),
sub: (a, b) => b !== undefined ? int(a[0] - b[1], a[1] - b[0]) : int(-a[1], -a[0]),
mul: (a, b) => range(a[0] * b[0], a[0] * b[1], a[1] * b[0], a[1] * b[1]),
div: (a, b) => hasZero(b) ? WHOLE : range(a[0] / b[0], a[0] / b[1], a[1] / b[0], a[1] / b[1]),
abs: (a) => {
if (hasZero(a))
return int(0, Math.max(-a[0], a[1]));
return range(Math.abs(a[0]), Math.abs(a[1]));
},
round: (a) => int(Math.round(a[0]), Math.round(a[1])),
floor: (a) => int(Math.floor(a[0]), Math.floor(a[1])),
ceil: (a) => int(Math.ceil(a[0]), Math.ceil(a[1])),
max: (...args) => int(Math.max(...args.map(a => a[0])), Math.max(...args.map(a => a[1]))),
min: (...args) => int(Math.min(...args.map(a => a[0])), Math.min(...args.map(a => a[1]))),
mod: (a, b) => {
if (isEmpty(a) || isEmpty(b))
return EMPTY;
let n = a[0] / (a[0] < 0 ? b[0] : b[1]);
n = (n < 0) ? Math.ceil(n) : Math.floor(n);
return interval.sub(a, interval.mul(b, [n, n])); // a mod b = a - n * b
},
lcm: (...args) => range(fermat.lcm(...args.map(a => a[0]))),
gcd: (...args) => range(fermat.gcd(...args.map(a => a[0]))),
gcf: (...args) => interval.gcd(...args),
sup: (a, b) => pow(a, b),
log: (a, b) => {
if (b !== undefined)
interval.div(interval.log(a), interval.log(b));
return int(a[0] <= 0 ? -Infinity : Math.log(a[0]), Math.log(a[1]));
},
exp: (a) => pow([Math.E, Math.E], a),
ln: (a) => interval.log(a),
sqrt: (a) => pow(a, [0.5, 0.5]),
root: (a, b) => pow(a, interval.div([1, 1], b)),
sin: (a) => interval.cos(interval.sub(a, [HALF_PI, HALF_PI])),
cos: (a) => {
if (isEmpty(a) || isInfinite(a))
return EMPTY;
if (width(a) >= TWO_PI - Number.EPSILON)
return [-1, 1];
a = intervalMod(a);
if (a[0] >= Math.PI)
return interval.sub(interval.cos(interval.sub(a, [Math.PI, Math.PI])));
// Now we know that 0 <= a[0] < pi.
if (a[1] <= Math.PI)
return int(Math.cos(a[1]), Math.cos(a[0]));
if (a[1] <= TWO_PI)
return int(-1, Math.max(Math.cos(a[1]), Math.cos(a[0])));
return int(-1, 1);
},
tan: (a) => {
if (isEmpty(a) || isInfinite(a))
return EMPTY;
a = intervalMod(a, Math.PI);
if (a[0] >= HALF_PI)
a = interval.sub(a, [Math.PI, Math.PI]);
if (a[0] <= -HALF_PI || a[1] >= HALF_PI)
return WHOLE;
return int(Math.tan(a[0]), Math.tan(a[1]));
},
sec: (a) => interval.div([1, 1], interval.cos(a)),
csc: (a) => interval.div([1, 1], interval.sin(a)),
cot: (a) => interval.div([1, 1], interval.tan(a)),
cosec: (a) => interval.csc(a),
cotan: (a) => interval.cot(a),
arcsin: (a) => {
if (isEmpty(a) || a[1] < -1 || a[0] > 1)
return EMPTY;
return int(a[0] <= -1 ? -HALF_PI : Math.asin(a[0]), a[1] >= 1 ? HALF_PI : Math.asin(a[1]));
},
arccos: (a) => {
if (isEmpty(a) || a[1] < -1 || a[0] > 1)
return EMPTY;
return int(a[1] >= 1 ? 0 : Math.acos(a[1]), a[0] <= -1 ? Math.PI : Math.acos(a[0]));
},
arctan: (a) => int(Math.atan(a[0]), Math.atan(a[1])),
sinh: (a) => int(Math.sinh(a[0]), Math.sinh(a[1])),
cosh: (a) => {
if (a[1] < 0)
return int(Math.cosh(a[1]), Math.cosh(a[0]));
if (a[0] > 0)
return int(Math.cosh(a[0]), Math.cosh(a[1]));
return int(1, Math.cosh(Math.max(-a[0], a[1])));
},
tanh: (a) => int(Math.tanh(a[0]), Math.tanh(a[1])),
sech: (a) => interval.div([1, 1], interval.cosh(a)),
csch: (a) => interval.div([1, 1], interval.sinh(a)),
coth: (a) => interval.div([1, 1], interval.tanh(a)),
cosech: (a) => interval.csch(a),
};
// =============================================================================
const PRECEDENCE = core.words('+ − * × · / ÷ // sup sub subsup');

@@ -382,33 +571,38 @@ const SUBSUP = core.words('sub sup subsup');

return vars[this.fn](...args);
switch (this.fn) {
case '+':
return args.reduce((a, b) => a + b, 0);
case '−':
return (args.length > 1) ? args[0] - args[1] : -args[0];
case '*':
case '·':
case '×':
return args.reduce((a, b) => a * b, 1);
case '/':
return args[0] / args[1];
case 'sin':
return Math.sin(args[0]);
case 'cos':
return Math.sin(args[0]);
case 'tan':
return Math.sin(args[0]);
case 'log':
return Math.log(args[0]) / Math.log(args[1] || Math.E);
case 'sup':
return Math.pow(args[0], args[1]);
case 'sqrt':
return Math.sqrt(args[0]);
case 'root':
return Math.pow(args[0], 1 / args[1]);
case '(':
return args[0];
// TODO Implement for all functions
}
if (this.fn === '+')
return evaluate.add(...args);
if (this.fn === '−')
return evaluate.sub(...args);
if (['*', '·', '×'].includes(this.fn))
return evaluate.mul(...args);
if (this.fn === '/')
return evaluate.div(...args);
if (this.fn === 'sup')
return evaluate.sup(...args);
if (isSpecialFunction(this.fn))
return evaluate[this.fn](...args);
if (this.fn === '(')
return args[0];
throw ExprError.undefinedFunction(this.fn);
}
interval(vars = {}) {
if (this.fn in vars)
return core.repeat(this.evaluate(vars), 2);
const args = this.args.map(a => a.interval(vars));
if (this.fn === '+')
return interval.add(...args);
if (this.fn === '−')
return interval.sub(...args);
if (['*', '·', '×'].includes(this.fn))
return interval.mul(...args);
if (this.fn === '/')
return interval.div(...args);
if (this.fn === 'sup')
return interval.sup(...args);
if (isSpecialFunction(this.fn))
return interval[this.fn](...args);
if (this.fn === '(')
return args[0];
throw ExprError.undefinedFunction(this.fn);
}
substitute(vars = {}) {

@@ -844,4 +1038,3 @@ return new ExprFunction(this.fn, this.args.map(a => a.substitute(vars)));

}
// Match comparison and division operators.
findBinaryFunction(tokens, '= < > ≤ ≥');
// Match division operators.
findBinaryFunction(tokens, '// ÷');

@@ -859,2 +1052,4 @@ // Match multiplication operators.

tokens = findAssociativeFunction(tokens, '+');
// Match comparison operators.
findBinaryFunction(tokens, '= < > ≤ ≥');
if (tokens.length > 1)

@@ -914,2 +1109,3 @@ throw ExprError.invalidExpression();

exports.ExprError = ExprError;
exports.ExprFunction = ExprFunction;
exports.Expression = Expression;

@@ -1,3 +0,3 @@

import { words, unique, flatten, isOneOf, join, last, cache } from '@mathigon/core';
import { nearlyEquals } from '@mathigon/fermat';
import { words, repeat, unique, flatten, isOneOf, join, last, cache } from '@mathigon/core';
import { lcm, gcd, isBetween, nearlyEquals } from '@mathigon/fermat';

@@ -163,7 +163,8 @@ // =============================================================================

}
const SPECIAL = new Set(['sin', 'cos', 'tan', 'sec', 'csc', 'cot', 'arcsin',
'arccos', 'arctan', 'sinh', 'cosh', 'tanh', 'sech', 'csch', 'coth', 'exp',
'log', 'ln', 'det', 'dim', 'mod', 'gcd', 'lcm', 'min', 'max']);
const SPECIAL_FUNCTIONS = ['abs', 'round', 'floor', 'ceil', 'max', 'min', 'mod',
'lcm', 'gcd', 'gcf', 'log', 'exp', 'ln', 'sqrt', 'root', 'sin', 'cos', 'tan',
'sec', 'csc', 'cot', 'cosec', 'cotan', 'arcsin', 'arccos', 'arctan', 'sinh',
'cosh', 'tanh', 'sech', 'csch', 'coth', 'cosech'];
function isSpecialFunction(fn) {
return SPECIAL.has(fn);
return SPECIAL_FUNCTIONS.includes(fn);
}

@@ -204,2 +205,5 @@ const VOICE_STRINGS = {

}
interval(vars = {}) {
return [this.evaluate(vars), this.evaluate(vars)];
}
/** Substitutes a new expression for a variable. */

@@ -269,2 +273,11 @@ substitute(_vars = {}) {

}
interval(vars = {}) {
var _a;
const x = (_a = vars[this.i]) !== null && _a !== void 0 ? _a : CONSTANTS[this.i];
if (Array.isArray(x))
return x;
if (typeof x === 'number')
return [x, x];
throw ExprError.undefinedVariable(this.i);
}
toMathML() {

@@ -341,2 +354,178 @@ const variant = isSpecialFunction(this.i) ? ' mathvariant="normal"' : '';

// =============================================================================
const WHOLE = [-Infinity, Infinity];
const EMPTY = [NaN, NaN];
const HALF_PI = Math.PI / 2;
const TWO_PI = Math.PI * 2;
const int = (a, b) => [a - Number.EPSILON, b + Number.EPSILON];
const range = (...args) => int(Math.min(...args), Math.max(...args));
const width = (a) => Math.abs(a[1] - a[0]);
const isEmpty = (a) => isNaN(a[0]) || isNaN(a[1]);
const isInfinite = (a) => !isFinite(a[0]) && a[0] === a[1];
const contains = (a, v) => isBetween(v, a[0] - Number.EPSILON, a[1] + Number.EPSILON);
const hasZero = (a) => contains(a, 0);
// -----------------------------------------------------------------------------
// Standard Evaluation
const evaluate = {
add: (...args) => args.reduce((a, b) => a + b, 0),
sub: (...args) => (args.length > 1) ? args[0] - args[1] : -args[0],
mul: (...args) => args.reduce((a, b) => a * b, 1),
div: (a, b) => a / b,
abs: (a) => Math.abs(a),
round: (a) => Math.round(a),
floor: (a) => Math.floor(a),
ceil: (a) => Math.ceil(a),
max: (...args) => Math.max(...args),
min: (...args) => Math.min(...args),
mod: (a, b) => a % b,
lcm: (...args) => lcm(...args),
gcd: (...args) => gcd(...args),
gcf: (...args) => gcd(...args),
sup: (a, b) => Math.pow(a, b),
log: (a, b) => Math.log(a) / (b === undefined ? 1 : Math.log(b)),
exp: (a) => Math.exp(a),
ln: (a) => Math.log(a),
sqrt: (a) => Math.sqrt(a),
root: (a, b) => Math.pow(a, 1 / b),
sin: (a) => Math.sin(a),
cos: (a) => Math.cos(a),
tan: (a) => Math.tan(a),
sec: (a) => 1 / Math.cos(a),
csc: (a) => 1 / Math.sin(a),
cot: (a) => 1 / Math.tan(a),
cosec: (a) => evaluate.csc(a),
cotan: (a) => evaluate.cot(a),
arcsin: (a) => Math.asin(a),
arccos: (a) => Math.acos(a),
arctan: (a) => Math.atan(a),
sinh: (a) => Math.sinh(a),
cosh: (a) => Math.cosh(a),
tanh: (a) => Math.tanh(a),
sech: (a) => 1 / Math.cosh(a),
csch: (a) => 1 / Math.sinh(a),
coth: (a) => 1 / Math.tanh(a),
cosech: (a) => evaluate.csch(a),
};
// -----------------------------------------------------------------------------
// Utility Functions
/** Evaluate a^b */
function pow(a, b) {
// If the base a is positive:
if (a[0] > 0) {
if (b[0] >= 0)
return int(a[0] ** b[0], a[1] ** b[1]);
return range(a[0] ** b[0], a[0] ** b[1], a[1] ** b[0], a[1] ** b[1]);
}
// If the exponent b is an integer:
const k = b[0];
if (Number.isInteger(k) && k === b[1]) {
if (k === 0)
return [hasZero(a) ? 0 : 1, 1];
if (k % 2)
return int(a[0] ** k, a[1] ** k);
if (hasZero(a))
return [0, Math.max(a[0] ** k, a[1] ** k)];
return [a[1] ** k, a[0] ** k];
}
return EMPTY; // TODO Implement this!
}
/** Shift an interval so that a[0] lies between 0 and 2 Pi. */
function intervalMod(a, m = TWO_PI) {
const d = Math.floor(a[0] / m) * m;
return [a[0] - d, a[1] - d];
}
// -----------------------------------------------------------------------------
// Interval Evaluation
const interval = {
add: (a, b) => int(a[0] + b[0], a[1] + b[1]),
sub: (a, b) => b !== undefined ? int(a[0] - b[1], a[1] - b[0]) : int(-a[1], -a[0]),
mul: (a, b) => range(a[0] * b[0], a[0] * b[1], a[1] * b[0], a[1] * b[1]),
div: (a, b) => hasZero(b) ? WHOLE : range(a[0] / b[0], a[0] / b[1], a[1] / b[0], a[1] / b[1]),
abs: (a) => {
if (hasZero(a))
return int(0, Math.max(-a[0], a[1]));
return range(Math.abs(a[0]), Math.abs(a[1]));
},
round: (a) => int(Math.round(a[0]), Math.round(a[1])),
floor: (a) => int(Math.floor(a[0]), Math.floor(a[1])),
ceil: (a) => int(Math.ceil(a[0]), Math.ceil(a[1])),
max: (...args) => int(Math.max(...args.map(a => a[0])), Math.max(...args.map(a => a[1]))),
min: (...args) => int(Math.min(...args.map(a => a[0])), Math.min(...args.map(a => a[1]))),
mod: (a, b) => {
if (isEmpty(a) || isEmpty(b))
return EMPTY;
let n = a[0] / (a[0] < 0 ? b[0] : b[1]);
n = (n < 0) ? Math.ceil(n) : Math.floor(n);
return interval.sub(a, interval.mul(b, [n, n])); // a mod b = a - n * b
},
lcm: (...args) => range(lcm(...args.map(a => a[0]))),
gcd: (...args) => range(gcd(...args.map(a => a[0]))),
gcf: (...args) => interval.gcd(...args),
sup: (a, b) => pow(a, b),
log: (a, b) => {
if (b !== undefined)
interval.div(interval.log(a), interval.log(b));
return int(a[0] <= 0 ? -Infinity : Math.log(a[0]), Math.log(a[1]));
},
exp: (a) => pow([Math.E, Math.E], a),
ln: (a) => interval.log(a),
sqrt: (a) => pow(a, [0.5, 0.5]),
root: (a, b) => pow(a, interval.div([1, 1], b)),
sin: (a) => interval.cos(interval.sub(a, [HALF_PI, HALF_PI])),
cos: (a) => {
if (isEmpty(a) || isInfinite(a))
return EMPTY;
if (width(a) >= TWO_PI - Number.EPSILON)
return [-1, 1];
a = intervalMod(a);
if (a[0] >= Math.PI)
return interval.sub(interval.cos(interval.sub(a, [Math.PI, Math.PI])));
// Now we know that 0 <= a[0] < pi.
if (a[1] <= Math.PI)
return int(Math.cos(a[1]), Math.cos(a[0]));
if (a[1] <= TWO_PI)
return int(-1, Math.max(Math.cos(a[1]), Math.cos(a[0])));
return int(-1, 1);
},
tan: (a) => {
if (isEmpty(a) || isInfinite(a))
return EMPTY;
a = intervalMod(a, Math.PI);
if (a[0] >= HALF_PI)
a = interval.sub(a, [Math.PI, Math.PI]);
if (a[0] <= -HALF_PI || a[1] >= HALF_PI)
return WHOLE;
return int(Math.tan(a[0]), Math.tan(a[1]));
},
sec: (a) => interval.div([1, 1], interval.cos(a)),
csc: (a) => interval.div([1, 1], interval.sin(a)),
cot: (a) => interval.div([1, 1], interval.tan(a)),
cosec: (a) => interval.csc(a),
cotan: (a) => interval.cot(a),
arcsin: (a) => {
if (isEmpty(a) || a[1] < -1 || a[0] > 1)
return EMPTY;
return int(a[0] <= -1 ? -HALF_PI : Math.asin(a[0]), a[1] >= 1 ? HALF_PI : Math.asin(a[1]));
},
arccos: (a) => {
if (isEmpty(a) || a[1] < -1 || a[0] > 1)
return EMPTY;
return int(a[1] >= 1 ? 0 : Math.acos(a[1]), a[0] <= -1 ? Math.PI : Math.acos(a[0]));
},
arctan: (a) => int(Math.atan(a[0]), Math.atan(a[1])),
sinh: (a) => int(Math.sinh(a[0]), Math.sinh(a[1])),
cosh: (a) => {
if (a[1] < 0)
return int(Math.cosh(a[1]), Math.cosh(a[0]));
if (a[0] > 0)
return int(Math.cosh(a[0]), Math.cosh(a[1]));
return int(1, Math.cosh(Math.max(-a[0], a[1])));
},
tanh: (a) => int(Math.tanh(a[0]), Math.tanh(a[1])),
sech: (a) => interval.div([1, 1], interval.cosh(a)),
csch: (a) => interval.div([1, 1], interval.sinh(a)),
coth: (a) => interval.div([1, 1], interval.tanh(a)),
cosech: (a) => interval.csch(a),
};
// =============================================================================
const PRECEDENCE = words('+ − * × · / ÷ // sup sub subsup');

@@ -378,33 +567,38 @@ const SUBSUP = words('sub sup subsup');

return vars[this.fn](...args);
switch (this.fn) {
case '+':
return args.reduce((a, b) => a + b, 0);
case '−':
return (args.length > 1) ? args[0] - args[1] : -args[0];
case '*':
case '·':
case '×':
return args.reduce((a, b) => a * b, 1);
case '/':
return args[0] / args[1];
case 'sin':
return Math.sin(args[0]);
case 'cos':
return Math.sin(args[0]);
case 'tan':
return Math.sin(args[0]);
case 'log':
return Math.log(args[0]) / Math.log(args[1] || Math.E);
case 'sup':
return Math.pow(args[0], args[1]);
case 'sqrt':
return Math.sqrt(args[0]);
case 'root':
return Math.pow(args[0], 1 / args[1]);
case '(':
return args[0];
// TODO Implement for all functions
}
if (this.fn === '+')
return evaluate.add(...args);
if (this.fn === '−')
return evaluate.sub(...args);
if (['*', '·', '×'].includes(this.fn))
return evaluate.mul(...args);
if (this.fn === '/')
return evaluate.div(...args);
if (this.fn === 'sup')
return evaluate.sup(...args);
if (isSpecialFunction(this.fn))
return evaluate[this.fn](...args);
if (this.fn === '(')
return args[0];
throw ExprError.undefinedFunction(this.fn);
}
interval(vars = {}) {
if (this.fn in vars)
return repeat(this.evaluate(vars), 2);
const args = this.args.map(a => a.interval(vars));
if (this.fn === '+')
return interval.add(...args);
if (this.fn === '−')
return interval.sub(...args);
if (['*', '·', '×'].includes(this.fn))
return interval.mul(...args);
if (this.fn === '/')
return interval.div(...args);
if (this.fn === 'sup')
return interval.sup(...args);
if (isSpecialFunction(this.fn))
return interval[this.fn](...args);
if (this.fn === '(')
return args[0];
throw ExprError.undefinedFunction(this.fn);
}
substitute(vars = {}) {

@@ -840,4 +1034,3 @@ return new ExprFunction(this.fn, this.args.map(a => a.substitute(vars)));

}
// Match comparison and division operators.
findBinaryFunction(tokens, '= < > ≤ ≥');
// Match division operators.
findBinaryFunction(tokens, '// ÷');

@@ -855,2 +1048,4 @@ // Match multiplication operators.

tokens = findAssociativeFunction(tokens, '+');
// Match comparison operators.
findBinaryFunction(tokens, '= < > ≤ ≥');
if (tokens.length > 1)

@@ -908,2 +1103,2 @@ throw ExprError.invalidExpression();

export { ExprElement, ExprError, Expression };
export { ExprElement, ExprError, ExprFunction, Expression };

@@ -10,1 +10,2 @@ // =============================================================================

export {ExprElement} from './src/elements';
export {ExprFunction} from './src/functions';

16

package.json
{
"name": "@mathigon/hilbert",
"version": "0.6.2",
"version": "0.6.3",
"description": "JavaScript expression parsing, MathML rendering and CAS.",

@@ -33,12 +33,12 @@ "keywords": [

"devDependencies": {
"@rollup/plugin-typescript": "8.1.1",
"@rollup/plugin-typescript": "8.2.1",
"@types/tape": "4.13.0",
"rollup": "2.38.2",
"tape": "5.1.1",
"rollup": "2.44.0",
"tape": "5.2.2",
"ts-node": "9.1.1",
"tslib": "2.1.0",
"typescript": "4.1.3",
"@typescript-eslint/eslint-plugin": "4.14.1",
"@typescript-eslint/parser": "4.14.1",
"eslint": "7.19.0",
"typescript": "4.2.3",
"@typescript-eslint/eslint-plugin": "4.20.0",
"@typescript-eslint/parser": "4.20.0",
"eslint": "7.23.0",
"eslint-config-google": "0.14.0",

@@ -45,0 +45,0 @@ "eslint-plugin-import": "2.22.1"

@@ -7,4 +7,3 @@ {

"groupName": "lint",
"automerge": true,
"automergeType": "branch"
"automerge": true
}, {

@@ -14,25 +13,20 @@ "packagePatterns": ["^@mathigon"],

"schedule": ["at any time"],
"automerge": true,
"automergeType": "branch"
"automerge": true
}, {
"packagePatterns": ["ts-node", "^typescript"],
"groupName": "typescript",
"automerge": true,
"automergeType": "branch"
"automerge": true
}, {
"packagePatterns": ["^@types/"],
"groupName": "types",
"automerge": true,
"automergeType": "branch"
"automerge": true
}, {
"packagePatterns": ["^rollup", "^@rollup"],
"groupName": "rollup",
"automerge": true,
"automergeType": "branch"
"automerge": true
}, {
"updateTypes": ["patch", "pin", "digest"],
"groupName": "versions",
"automerge": true,
"automergeType": "branch"
"automerge": true
}]
}

@@ -8,2 +8,3 @@ // =============================================================================

import {Obj} from '@mathigon/core';
import {Interval} from './eval';
import {CONSTANTS, escape, isSpecialFunction, VOICE_STRINGS} from './symbols';

@@ -19,3 +20,3 @@ import {ExprError} from './errors';

export type CustomFunction = ((...args: number[]) => number);
export type VarMap = Obj<number|CustomFunction>;
export type VarMap = Obj<number|Interval|CustomFunction>;
export type ExprMap = Obj<ExprElement>;

@@ -35,2 +36,6 @@ export type MathMLMap = Obj<(...args: MathMLArgument[]) => string>;

interval(vars: VarMap = {}): Interval {
return [this.evaluate(vars), this.evaluate(vars)];
}
/** Substitutes a new expression for a variable. */

@@ -114,2 +119,9 @@ substitute(_vars: ExprMap = {}): ExprElement {

interval(vars: VarMap = {}): Interval {
const x = vars[this.i] ?? CONSTANTS[this.i];
if (Array.isArray(x)) return x;
if (typeof x === 'number') return [x, x];
throw ExprError.undefinedVariable(this.i);
}
toMathML() {

@@ -116,0 +128,0 @@ const variant = isSpecialFunction(this.i) ? ' mathvariant="normal"' : '';

@@ -7,3 +7,4 @@ // =============================================================================

import {unique, flatten, words, isOneOf, join} from '@mathigon/core';
import {unique, flatten, words, isOneOf, join, repeat} from '@mathigon/core';
import {evaluate, interval, Interval} from './eval';
import {collapseTerm} from './parser';

@@ -50,34 +51,25 @@ import {BRACKETS, escape, isSpecialFunction, VOICE_STRINGS} from './symbols';

const args = this.args.map(a => a.evaluate(vars));
if (this.fn in vars) return (vars[this.fn] as CustomFunction)(...args);
if (this.fn === '+') return evaluate.add(...args);
if (this.fn === '−') return evaluate.sub(...args);
if (['*', '·', '×'].includes(this.fn)) return evaluate.mul(...args);
if (this.fn === '/') return evaluate.div(...args);
if (this.fn === 'sup') return evaluate.sup(...args);
if (isSpecialFunction(this.fn)) return evaluate[this.fn](...args);
if (this.fn === '(') return args[0];
throw ExprError.undefinedFunction(this.fn);
}
switch (this.fn) {
case '+':
return args.reduce((a, b) => a + b, 0);
case '−':
return (args.length > 1) ? args[0] - args[1] : -args[0];
case '*':
case '·':
case '×':
return args.reduce((a, b) => a * b, 1);
case '/':
return args[0] / args[1];
case 'sin':
return Math.sin(args[0]);
case 'cos':
return Math.sin(args[0]);
case 'tan':
return Math.sin(args[0]);
case 'log':
return Math.log(args[0]) / Math.log(args[1] || Math.E);
case 'sup':
return Math.pow(args[0], args[1]);
case 'sqrt':
return Math.sqrt(args[0]);
case 'root':
return Math.pow(args[0], 1 / args[1]);
case '(':
return args[0];
// TODO Implement for all functions
}
interval(vars: VarMap = {}): Interval {
if (this.fn in vars) return repeat(this.evaluate(vars), 2) as Interval;
const args = this.args.map(a => a.interval(vars));
if (this.fn === '+') return interval.add(...args);
if (this.fn === '−') return interval.sub(...args);
if (['*', '·', '×'].includes(this.fn)) return interval.mul(...args);
if (this.fn === '/') return interval.div(...args);
if (this.fn === 'sup') return interval.sup(...args);
if (isSpecialFunction(this.fn)) return interval[this.fn](...args);
if (this.fn === '(') return args[0];
throw ExprError.undefinedFunction(this.fn);

@@ -84,0 +76,0 @@ }

@@ -277,4 +277,3 @@ // =============================================================================

// Match comparison and division operators.
findBinaryFunction(tokens, '= < > ≤ ≥');
// Match division operators.
findBinaryFunction(tokens, '// ÷');

@@ -296,4 +295,7 @@

// Match comparison operators.
findBinaryFunction(tokens, '= < > ≤ ≥');
if (tokens.length > 1) throw ExprError.invalidExpression();
return tokens[0];
}

@@ -135,9 +135,10 @@ // =============================================================================

const SPECIAL = new Set<string>(
['sin', 'cos', 'tan', 'sec', 'csc', 'cot', 'arcsin',
'arccos', 'arctan', 'sinh', 'cosh', 'tanh', 'sech', 'csch', 'coth', 'exp',
'log', 'ln', 'det', 'dim', 'mod', 'gcd', 'lcm', 'min', 'max']);
const SPECIAL_FUNCTIONS = ['abs', 'round', 'floor', 'ceil', 'max', 'min', 'mod',
'lcm', 'gcd', 'gcf', 'log', 'exp', 'ln', 'sqrt', 'root', 'sin', 'cos', 'tan',
'sec', 'csc', 'cot', 'cosec', 'cotan', 'arcsin', 'arccos', 'arctan', 'sinh',
'cosh', 'tanh', 'sech', 'csch', 'coth', 'cosech'] as const;
export type SpecialFunction = typeof SPECIAL_FUNCTIONS[number];
export function isSpecialFunction(fn: string) {
return SPECIAL.has(fn);
export function isSpecialFunction(fn: string): fn is SpecialFunction {
return SPECIAL_FUNCTIONS.includes(fn as SpecialFunction);
}

@@ -144,0 +145,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc