nerdamer-ts
Version:
javascript light-weight symbolic math expression evaluator
393 lines • 12.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getQuadrant = exports.Frac = void 0;
const Utils_1 = require("../Core/Utils");
const decimal_js_1 = __importDefault(require("decimal.js"));
const bigInt_1 = __importDefault(require("../3rdparty/bigInt"));
const Scientific_1 = __importDefault(require("./Scientific"));
const Errors_1 = require("../Core/Errors");
const Settings_1 = require("../Settings");
class Frac {
constructor(n) {
if (n instanceof Frac)
return n;
if (n === undefined)
return this;
try {
if ((0, Utils_1.isInt)(n)) {
try {
this.num = (0, bigInt_1.default)(n);
this.den = (0, bigInt_1.default)(1);
}
catch (e) {
return Frac.simple(n);
}
}
else {
var frac = n instanceof decimal_js_1.default ? Fraction.quickConversion(n) : Fraction.convert(n);
this.num = new bigInt_1.default(frac[0]);
this.den = new bigInt_1.default(frac[1]);
}
}
catch (e) {
return Frac.simple(n);
}
}
//safe to use with negative numbers or other types
static create(n) {
if (n instanceof Frac)
return n;
n = n.toString();
var is_neg = n.charAt(0) === '-'; //check if it's negative
if (is_neg)
n = n.substr(1, n.length - 1); //remove the sign
var frac = new Frac(n);
//put the sign back
if (is_neg)
frac.negate();
return frac;
}
static isFrac(o) {
return (o instanceof Frac);
}
static quick(n, d) {
var frac = new Frac();
frac.num = new bigInt_1.default(n);
frac.den = new bigInt_1.default(d);
return frac;
}
static simple(n) {
var nstr = String((0, Utils_1.scientificToDecimal)(n)), m_dc = nstr.split('.'), num = m_dc.join(''), den = 1, l = (m_dc[1] || '').length;
for (var i = 0; i < l; i++)
den += '0';
var frac = Frac.quick(num, den);
return frac.simplify();
}
multiply(m) {
if (this.isOne()) {
return m.clone();
}
if (m.isOne()) {
return this.clone();
}
var c = this.clone();
c.num = c.num.multiply(m.num);
c.den = c.den.multiply(m.den);
return c.simplify();
}
divide(m) {
if (m.equals(0))
throw new Errors_1.DivisionByZero('Division by zero not allowed!');
return this.clone().multiply(m.clone().invert()).simplify();
}
subtract(m) {
return this.clone().add(m.clone().neg());
}
neg() {
this.num = this.num.multiply(-1);
return this;
}
add(m) {
var n1 = this.den, n2 = m.den, c = this.clone();
var a = c.num, b = m.num;
if (n1.equals(n2)) {
c.num = a.add(b);
}
else {
c.num = a.multiply(n2).add(b.multiply(n1));
c.den = n1.multiply(n2);
}
return c.simplify();
}
mod(m) {
var a = this.clone(), b = m.clone();
//make their denominators even and return the mod of their numerators
a.num = a.num.multiply(b.den);
a.den = a.den.multiply(b.den);
b.num = b.num.multiply(this.den);
b.den = b.den.multiply(this.den);
a.num = a.num.mod(b.num);
return a.simplify();
}
simplify() {
var gcd = bigInt_1.default.gcd(this.num, this.den);
this.num = this.num.divide(gcd);
this.den = this.den.divide(gcd);
return this;
}
clone() {
var m = new Frac();
m.num = new bigInt_1.default(this.num);
m.den = new bigInt_1.default(this.den);
return m;
}
decimal(prec) {
var sign = this.num.isNegative() ? '-' : '';
if (this.num.equals(this.den)) {
return '1';
}
//go plus one for rounding
prec = prec || Settings_1.Settings.PRECISION;
prec++;
var narr = [], n = this.num.abs(), d = this.den;
for (var i = 0; i < prec; i++) {
var w = n.divide(d), //divide out whole
r = n.subtract(w.multiply(d)); //get remainder
narr.push(w);
if (r.equals(0))
break;
n = r.times(10); //shift one dec place
}
var whole = narr.shift();
if (narr.length === 0) {
return sign + whole.toString();
}
if (i === prec) {
var lt = [];
//get the last two so we can round it
for (var i = 0; i < 2; i++)
lt.unshift(narr.pop());
//put the last digit back by rounding the last two
narr.push(Math.round(lt.join('.')));
}
var dec = whole.toString() + '.' + narr.join('');
return sign + dec;
}
toDecimal(prec) {
prec = prec || Settings_1.Settings.PRECISION;
if (prec) {
return this.decimal(prec);
}
else
return this.num / this.den;
}
qcompare(n) {
return [this.num.multiply(n.den), n.num.multiply(this.den)];
}
equals(n) {
if (!isNaN(n))
n = new Frac(n);
var q = this.qcompare(n);
return q[0].equals(q[1]);
}
absEquals(n) {
if (!isNaN(n))
n = new Frac(n);
var q = this.qcompare(n);
return q[0].abs().equals(q[1]);
}
//lazy check to be fixed. Sufficient for now but will cause future problems
greaterThan(n) {
if (!isNaN(n))
n = new Frac(n);
var q = this.qcompare(n);
return q[0].gt(q[1]);
}
gte(n) {
return this.greaterThan(n) || this.equals(n);
}
lte(n) {
return this.lessThan(n) || this.equals(n);
}
lessThan(n) {
if (!isNaN(n))
n = new Frac(n);
var q = this.qcompare(n);
return q[0].lt(q[1]);
}
isInteger() {
return this.den.equals(1);
}
negate() {
this.num = this.num.multiply(-1);
return this;
}
invert() {
var t = this.den;
//why invert 0/1? It'll become 1/0 and that's a lie.
if (!this.num.equals(0)) {
var isnegative = this.num.isNegative();
this.den = this.num.abs();
this.num = t;
if (isnegative)
this.num = this.num.multiply(-1);
}
return this;
}
isOne() {
return this.num.equals(1) && this.den.equals(1);
}
sign() {
return this.num.isNegative() ? -1 : 1;
}
abs() {
this.num = this.num.abs();
return this;
}
gcd(f) {
return Frac.quick(bigInt_1.default.gcd(f.num, this.num), bigInt_1.default.lcm(f.den, this.den));
}
toString() {
return !this.den.equals(1) ? this.num.toString() + '/' + this.den.toString() : this.num.toString();
}
valueOf() {
// if (this.num == 24) throw new Error(999)
if (Settings_1.Settings.USE_BIG)
return new decimal_js_1.default(this.num.toString()).div(new decimal_js_1.default(this.den.toString()));
return this.num / this.den;
}
isNegative() {
return this.toDecimal() < 0;
}
}
exports.Frac = Frac;
/* "STATIC" */
// converts a number to a fraction.
var Fraction = {
/**
* Converts a decimal to a fraction
* @param {number} value
* @param {object} opts
* @returns {Array} - an array containing the denominator and the numerator
*/
convert: function (value, opts) {
var frac;
if (value === 0) {
frac = [0, 1];
}
else {
if (value < 1e-6 || value > 1e20) {
var qc = this.quickConversion(Number(value));
if (qc[1] <= 1e20) {
var abs = Math.abs(value);
var sign = value / abs;
frac = this.fullConversion(abs.toFixed((qc[1] + '').length - 1));
frac[0] = frac[0] * sign;
}
else {
frac = qc;
}
}
else {
frac = this.fullConversion(value);
}
}
return frac;
},
/**
* If the fraction is too small or too large this gets called instead of fullConversion method
* @param {number} dec
* @returns {Array} - an array containing the denominator and the numerator
*/
quickConversion: function (value) {
var stripSign = function (s) {
// Explicitely convert to a string
if (typeof s !== 'string') {
s = s.toString();
}
var sign = '';
// Remove and store the sign
var start = s.charAt(0);
if (start === '-') {
s = s.substr(1, s.length);
sign = '-';
}
else if (start === '+') {
// Just remove the plus sign
s = s.substr(1, s.length);
}
return {
sign: sign,
value: s
};
};
function convert(value) {
// Explicitely convert to a decimal
if (Scientific_1.default.isScientific(value)) {
value = (0, Utils_1.scientificToDecimal)(value);
}
// Split the value into the sign and the value
var nparts = stripSign(value);
// Split it at the decimal. We'll refer to it as the coeffient parts
var cparts = nparts.value.split('.');
// Combine the entire number by removing leading zero and adding the decimal part
// This would be teh same as moving the decimal point to the end
var num;
// We're dealing with integers
if (cparts.length === 1) {
num = cparts[0];
}
else {
num = cparts[0] + cparts[1];
}
var n = cparts[1] ? cparts[1].length : 0;
// Generate the padding for the zeros
var den = `1${'0'.repeat(n)}`;
if (num !== '0') {
num = num.replace(/^0+/, '');
}
return [nparts.sign + num, den];
}
return convert(value);
},
/**
* Returns a good approximation of a fraction. This method gets called by convert
* http://mathforum.org/library/drmath/view/61772.html
* Decimal To Fraction Conversion - A Simpler Version
* Dr Peterson
* @param {number} dec
* @returns {Array} - an array containing the denominator and the numerator
*/
fullConversion: function (dec) {
var done = false;
// you can adjust the epsilon to a larger number if you don't need very high precision
var n1 = 0, d1 = 1, n2 = 1, d2 = 0, n = 0, q = dec, epsilon = 1e-16;
while (!done) {
n++;
if (n > 10000) {
done = true;
}
var a = Math.floor(q);
var num = n1 + a * n2;
var den = d1 + a * d2;
var e = (q - a);
if (e < epsilon) {
done = true;
}
q = 1 / e;
n1 = n2;
d1 = d2;
n2 = num;
d2 = den;
if (Math.abs(num / den - dec) < epsilon || n > 30) {
done = true;
}
}
return [num, den];
}
};
//Depends on Fraction
/**
* Gets the quadrant of the trig function
* @param {Frac} m
* @returns {Int}
*/
function getQuadrant(m) {
var v = m % 2, quadrant;
if (v < 0)
v = 2 + v; //put it in terms of pi
if (v >= 0 && v <= 0.5)
quadrant = 1;
else if (v > 0.5 && v <= 1)
quadrant = 2;
else if (v > 1 && v <= 1.5)
quadrant = 3;
else
quadrant = 4;
return quadrant;
}
exports.getQuadrant = getQuadrant;
//# sourceMappingURL=Frac.js.map