nerdamer-ts
Version:
javascript light-weight symbolic math expression evaluator
261 lines (259 loc) • 11.8 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.text = void 0;
const bigInt_1 = __importDefault(require("../3rdparty/bigInt"));
const Scientific_1 = __importDefault(require("../Types/Scientific"));
const Settings_1 = require("../Settings");
const Utils_1 = require("./Utils");
const Groups_1 = require("../Types/Groups");
/**
* This method will return a hash or a text representation of a Symbol, Matrix, or Vector.
* If all else fails it *assumes* the object has a toString method and will call that.
*
* @param {Object} obj
* @param {string | undefined} option get is as a hash
* @param {int | undefined} useGroup
* @param {int | undefined} decp
* @returns {String}
*/
function text(obj, option = undefined, useGroup = undefined, decp = undefined) {
var asHash = option === 'hash',
//whether to wrap numbers in brackets
wrapCondition = undefined, opt = asHash ? undefined : option, asDecimal = opt === 'decimal' || opt === 'decimals';
if (asDecimal && typeof decp === 'undefined')
decp = 16;
function toString(obj) {
switch (option) {
case 'decimals':
case 'decimal':
wrapCondition = wrapCondition || function (str) {
return false;
};
return obj.valueOf();
case 'recurring':
wrapCondition = wrapCondition || function (str) {
return str.indexOf("'") !== -1;
};
var str = obj.toString();
//verify that the string is actually a fraction
var frac = /^-?\d+(?:\/\d+)?$/.exec(str);
if (frac.length === 0)
return str;
//split the fraction into the numerator and denominator
var parts = frac[0].split('/');
var negative = false;
var m = Number(parts[0]);
if (m < 0) {
m = -m;
negative = true;
}
var n = Number(parts[1]);
if (!n)
n = 1;
//https://softwareengineering.stackexchange.com/questions/192070/what-is-a-efficient-way-to-find-repeating-decimal#comment743574_192081
var quotient = Math.floor(m / n), c = 10 * (m - quotient * n);
quotient = quotient.toString() + ".";
while (c && c < n) {
c *= 10;
quotient += "0";
}
var digits = "", passed = [], i = 0;
while (true) {
if (typeof passed[c] !== 'undefined') {
var prefix = digits.slice(0, passed[c]), cycle = digits.slice(passed[c]), result = quotient + prefix + "'" + cycle + "'";
return (negative ? "-" : "") + result.replace("'0'", "").replace(/\.$/, "");
}
var q = Math.floor(c / n), r = c - q * n;
passed[c] = i;
digits += q.toString();
i += 1;
c = 10 * r;
}
case 'mixed':
wrapCondition = wrapCondition || function (str) {
return str.indexOf('/') !== -1;
};
var str = obj.toString();
//verify that the string is actually a fraction
var frac = /^-?\d+(?:\/\d+)?$/.exec(str);
if (frac.length === 0)
return str;
//split the fraction into the numerator and denominator
var parts = frac[0].split('/');
var numer = new bigInt_1.default(parts[0]);
var denom = new bigInt_1.default(parts[1]);
if (denom.equals(0))
denom = new bigInt_1.default(1);
//return the quotient plus the remainder
var divmod = numer.divmod(denom);
var quotient = divmod.quotient;
var remainder = divmod.remainder;
var operator = parts[0][0] === '-' || quotient.equals(0) || remainder.equals(0) ? '' : '+';
return (quotient.equals(0) ? '' : quotient.toString()) + operator + (remainder.equals(0) ? '' : (remainder.toString() + '/' + parts[1]));
case 'scientific':
wrapCondition = wrapCondition || function (str) {
return false;
};
return new Scientific_1.default(obj.valueOf()).toString(Settings_1.Settings.SCIENTIFIC_MAX_DECIMAL_PLACES);
default:
wrapCondition = wrapCondition || function (str) {
return str.indexOf('/') !== -1;
};
return obj.toString();
}
}
//if the object is a symbol
if ((0, Utils_1.isSymbol)(obj)) {
var multiplier = '', power = '', sign = '', group = obj.group || useGroup, value = obj.value;
//if the value is to be used as a hash then the power and multiplier need to be suppressed
if (!asHash) {
//use asDecimal to get the object back as a decimal
var om = toString(obj.multiplier);
if (om == '-1' && String(obj.multiplier) === '-1') {
sign = '-';
om = '1';
}
//only add the multiplier if it's not 1
if (om != '1')
multiplier = om;
//use asDecimal to get the object back as a decimal
var p = obj.power ? toString(obj.power) : '';
//only add the multiplier
if (p != '1') {
//is it a symbol
if ((0, Utils_1.isSymbol)(p)) {
power = text(p, opt);
}
else {
power = p;
}
}
}
switch (group) {
case Groups_1.Groups.N:
multiplier = '';
//round if requested
var m = decp && asDecimal ? obj.multiplier.toDecimal(decp) : toString(obj.multiplier);
//if it's numerical then all we need is the multiplier
value = String(obj.multiplier) == '-1' ? '1' : m;
power = '';
break;
case Groups_1.Groups.PL:
value = obj.collectSymbols().map(function (x) {
var txt = text(x, opt, useGroup, decp);
if (txt == '0')
txt = '';
return txt;
}).sort().join('+').replace(/\+\-/g, '-');
break;
case Groups_1.Groups.CP:
value = obj.collectSymbols().map(function (x) {
var txt = text(x, opt, useGroup, decp);
if (txt == '0')
txt = '';
return txt;
}).sort().join('+').replace(/\+\-/g, '-');
break;
case Groups_1.Groups.CB:
value = obj.collectSymbols(function (symbol) {
var g = symbol.group;
//both groups will already be in brackets if their power is greater than 1
//so skip it.
if ((g === Groups_1.Groups.PL || g === Groups_1.Groups.CP) && (symbol.power.equals(1) && symbol.multiplier.equals(1))) {
return (0, Utils_1.inBrackets)(text(symbol, opt));
}
return text(symbol, opt);
}).join('*');
break;
case Groups_1.Groups.EX:
var pg = obj.previousGroup, pwg = obj.power.group;
//Groups.PL are the exception. It's simpler to just collect and set the value
if (pg === Groups_1.Groups.PL)
value = obj.collectSymbols(text, opt).join('+').replace('+-', '-');
if (!(pg === Groups_1.Groups.N || pg === Groups_1.Groups.S || pg === Groups_1.Groups.FN) && !asHash) {
value = (0, Utils_1.inBrackets)(value);
}
if ((pwg === Groups_1.Groups.CP || pwg === Groups_1.Groups.CB || pwg === Groups_1.Groups.PL || obj.power.multiplier.toString() != '1') && power) {
power = (0, Utils_1.inBrackets)(power);
}
break;
}
if (group === Groups_1.Groups.FN) {
value = obj.fname + (0, Utils_1.inBrackets)(obj.args.map(function (symbol) {
return text(symbol, opt);
}).join(','));
}
/*
FIXME: Currently there is no way to set CUSTOM_OPERATORS outside of core, is this code still needed?
//TODO: Needs to be more efficient. Maybe.
if (group === Groups.FN && obj.fname in CUSTOM_OPERATORS) {
var a = text(obj.args[0]);
var b = text(obj.args[1]);
if (obj.args[0].isComposite()) //preserve the brackets
a = inBrackets(a);
if (obj.args[1].isComposite()) //preserve the brackets
b = inBrackets(b);
value = a + deps.CUSTOM_OPERATORS[obj.fname] + b;
}
*/
//wrap the power since / is less than ^
//TODO: introduce method call isSimple
if (power && group !== Groups_1.Groups.EX && wrapCondition(power)) {
power = (0, Utils_1.inBrackets)(power);
}
//the following groups are held together by plus or minus. They can be raised to a power or multiplied
//by a multiplier and have to be in brackets to preserve the order of precedence
if (((group === Groups_1.Groups.CP || group === Groups_1.Groups.PL) && (multiplier && multiplier != '1' || sign === '-'))
|| ((group === Groups_1.Groups.CB || group === Groups_1.Groups.CP || group === Groups_1.Groups.PL) && (power && power != '1'))
|| !asHash && group === Groups_1.Groups.P && value == -1
|| obj.fname === Settings_1.Settings.PARENTHESIS) {
value = (0, Utils_1.inBrackets)(value);
}
if (decp && (option === 'decimal' || option === 'decimals' && multiplier)) {
multiplier = (0, Utils_1.nround)(multiplier, decp);
}
//add the sign back
var c = sign + multiplier;
if (multiplier && wrapCondition(multiplier))
c = (0, Utils_1.inBrackets)(c);
if (power < 0)
power = (0, Utils_1.inBrackets)(power);
//add the multiplication back
if (multiplier)
c = c + '*';
if (power) {
if (value === 'e' && Settings_1.Settings.E_TO_EXP) {
return c + 'exp' + (0, Utils_1.inBrackets)(power);
}
power = Settings_1.Settings.POWER_OPERATOR + power;
}
//this needs serious rethinking. Must fix
if (group === Groups_1.Groups.EX && value.charAt(0) === '-') {
value = (0, Utils_1.inBrackets)(value);
}
var cv = c + value;
if (obj.parens) {
cv = (0, Utils_1.inBrackets)(cv);
}
return cv + power;
}
else if ((0, Utils_1.isVector)(obj)) {
var l = obj.elements.length, c = [];
for (var i = 0; i < l; i++)
c.push(obj.elements[i].text(option));
return '[' + c.join(',') + ']';
}
else {
try {
return obj.toString();
}
catch (e) {
return '';
}
}
}
exports.text = text;
//# sourceMappingURL=Text.js.map