nerdamer-prime
Version:
javascript light-weight symbolic math library
1,575 lines (1,511 loc) • 118 kB
JavaScript
/* global expect */
const nerdamer = require('../nerdamer.core.js');
const utils = require('./support/utils');
const _ = utils.toFixed;
const core = nerdamer.getCore();
const { round } = core.Utils;
const { block } = core.Utils;
// , x=2.1, y=3.3, z=1, a=7.42
const values = {
x: 2.1,
y: 3.3,
z: 1,
a: 7.42,
};
describe('Nerdamer core', () => {
it('should perform simple arithmetic', () => {
// Given
const testCases = [
{
given: '((((((1+1))))))',
expected: '2',
expectedValue: '2',
},
{
given: '((((((1+1))+4)))+3)',
expected: '9',
expectedValue: '9',
},
{
given: '1+1',
expected: '2',
expectedValue: '2',
},
{
given: '4^2',
expected: '16',
expectedValue: '16',
},
{
given: '2*-4',
expected: '-8',
expectedValue: '-8',
},
{
given: '2+(3/4)',
expected: '11/4',
expectedValue: '2.75',
},
{
given: '2/3+2/3',
expected: '4/3',
expectedValue: '1.3333333333333333',
},
{
given: '6.5*2',
expected: '13',
expectedValue: '13',
},
{
given: '(11.85)^(1/2)',
expected: '(1/2)*sqrt(237)*sqrt(5)^(-1)',
expectedValue: '3.4423828956117015',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate().text('decimals');
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(round(value), 14).toEqual(round(testCases[i].expectedValue), 14);
}
});
it('should handle minus sign properly', () => {
// Given
const cases = [
{
given: '0-4',
expected: '-4',
},
{
given: '-(4)',
expected: '-4',
},
{
given: '3*-(4)',
expected: '-12',
},
{
given: '-3*-(4)',
expected: '12',
},
{
given: '-(3*-(4))',
expected: '12',
},
{
given: '-(-3*-(4))',
expected: '-12',
},
{
given: '-(3)-3',
expected: '-6',
},
{
given: '3^-1^-1',
expected: '1/3',
},
{
given: '-1',
expected: '-1',
},
{
given: '--1',
expected: '1',
},
{
given: '8-1',
expected: '7',
},
{
given: '(-1)',
expected: '-1',
},
{
given: '-(1)-1',
expected: '-2',
},
{
given: '-(-1-1)',
expected: '2',
},
{
given: '-(-1-+1)^2',
expected: '-4',
},
{
given: '-(-1-1+1)',
expected: '1',
},
{
given: '-(1)--(1-1--1)',
expected: '0',
},
{
given: '-(-(1))-(--1)',
expected: '0',
},
{
given: '5^-3',
expected: '1/125',
},
{
given: '5^---3',
expected: '1/125',
},
{
given: '5^-(1--2)',
expected: '1/125',
},
{
given: '5^-(++1+--+2)',
expected: '1/125',
},
{
given: '(5^-(++1+--+2))^-2',
expected: '15625',
},
{
given: '(5^-3^2)',
expected: '1/1953125',
},
{
given: '(5^-3^-2)',
expected: '5^(-1/9)',
},
{
given: '-(5^-3^-2)^-3',
expected: '-5^(1/3)',
},
{
given: '-(--5*--7)',
expected: '-35',
},
{
given: '(-1)^(3/4)',
expected: '(-1)^(3/4)',
},
];
for (const k in cases) {
if (Object.hasOwn(cases, k)) {
// When
const parsed = nerdamer(cases[k].given);
// Then
expect(parsed.toString()).toEqual(cases[k].expected);
}
}
});
it('should perform simple calculations with variables', () => {
// Given
const testCases = [
{
given: 'x+x',
expected: '2*x',
expectedValue: '4.2',
},
{
given: 'x+1',
expected: '1+x',
expectedValue: '3.1',
},
{
given: 'x+y',
expected: 'x+y',
expectedValue: '5.4',
},
{
given: '-9-x+y-11',
expected: '-20-x+y',
expectedValue: '-18.8',
},
{
given: '(x+1)+(1+x)',
expected: '2+2*x',
expectedValue: '6.2',
},
{
given: '9+(x+1)-(1+x)',
expected: '9',
expectedValue: '9',
},
{
given: '0-x-2*x+6',
expected: '-3*x+6',
expectedValue: '-0.3',
},
{
given: '(x+y)+(a+x)',
expected: '2*x+a+y',
expectedValue: '14.92',
},
{
given: '7*(x+y)+2*(a+x)',
expected: '2*a+7*y+9*x',
expectedValue: '56.84',
},
{
given: 'x^y+x^y',
expected: '2*x^y',
expectedValue: '23.13948390048293',
},
{
given: 'x*x',
expected: 'x^2',
expectedValue: '4.41',
},
{
given: '-x*x',
expected: '-x^2',
expectedValue: '-4.41',
},
{
given: '-x*-x',
expected: 'x^2',
expectedValue: '4.41',
},
{
given: 'x-y',
expected: '-y+x',
expectedValue: '-1.2',
},
{
given: 'x-x',
expected: '0',
expectedValue: '0',
},
{
given: 'x*2',
expected: '2*x',
expectedValue: '4.2',
},
{
given: 'x*y',
expected: 'x*y',
expectedValue: '6.93',
},
{
given: 'x^x',
expected: 'x^x',
expectedValue: '4.749638091742242',
},
{
given: 'x^(x)',
expected: 'x^x',
expectedValue: '4.749638091742242',
},
{
given: 'y^y^3',
expected: 'y^y^3',
expectedValue: '4303635263255155700',
},
{
given: '(x-1)^2+(x-1)^2',
expected: '2*(-1+x)^2',
expectedValue: '2.4200000000000004',
},
{
given: '(-5(x/2-5)/4)^0',
expected: '1',
expectedValue: '1',
},
{
given: '((1+x)^(-2))+((1+x)^(-1))+((1+x)^(-1))+(1)',
expected: '(1+x)^(-2)+2*(1+x)^(-1)+1',
expectedValue: '1.74921956295526',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate(values).text('decimals');
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(round(value), 14).toEqual(round(testCases[i].expectedValue), 14);
}
});
it('should handle errors', () => {
const formulas = ['0/0', '0^0', '-Infinity+Infinity', 'Infinity/Infinity', 'Infinity^Infinity', '1^Infinity', 'Infinity^0', '(-Infinity)^0', 'Infinity*0'];
for (let i = 0; i < formulas.length; i++) {
expect(() => {
nerdamer(formulas[i]);
}).toThrowError();
}
});
it('should set postfix operators correctly', () => {
const nerdamerCore = nerdamer.getCore();
const parser = nerdamerCore.PARSER;
const { NerdamerSymbol } = nerdamerCore;
nerdamer.setOperator({
precedence: 4,
operator: '°',
postfix: true,
operation(x) {
return parser.divide(parser.multiply(x, new NerdamerSymbol('pi')), new NerdamerSymbol(180));
},
});
expect(nerdamer('x+1°+π+x').toString()).toEqual('(181/180)*pi+2*x');
});
it('should correctly calculate Infinity', () => {
// Given
const testCases = [
{
given: '0^Infinity',
expected: '0',
},
{
given: 'Infinity*Infinity',
expected: 'Infinity',
},
{
given: '-Infinity*Infinity',
expected: '-Infinity',
},
{
given: '-Infinity*-Infinity',
expected: 'Infinity',
},
{
given: '-a*-Infinity',
expected: 'Infinity*a',
},
{
given: '-a-Infinity',
expected: '-Infinity',
},
{
given: '-a*Infinity',
expected: '-Infinity*a',
},
{
given: '-a^Infinity',
expected: '-a^Infinity',
},
{
given: '-2^Infinity',
expected: '-Infinity',
},
{
given: '-2^-Infinity',
expected: '0',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
}
});
it('should calculate fib correctly', () => {
// Given
const testCases = [
{
given: 'fib(0)',
expected: '0',
},
{
given: 'fib(14)',
expected: '377',
},
{
given: 'fib(-14)',
expected: '-377',
},
{
given: 'fib(15)',
expected: '610',
},
{
given: 'fib(-15)',
expected: '610',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given).evaluate();
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
}
});
it('should calculate sinc correctly', () => {
// Given
const testCases = [
{
given: 'sinc(x)',
expected: 'sinc(x)',
eval: false,
},
{
given: 'sinc(0)',
expected: '1',
eval: true,
},
{
given: 'sinc(9)',
expected: '23287849/508568891',
eval: true,
},
{
given: 'sinc(x-a)-sin(x-a)/(x-a)',
expected: '0',
eval: true,
},
];
for (let i = 0; i < testCases.length; ++i) {
const testCase = testCases[i];
// When
const parsed = testCase.eval ? nerdamer(testCase.given).evaluate() : nerdamer(testCase.given);
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
}
});
it('should expand terms', () => {
// Given
const testCases = [
{
given: 'expand((9*y*x+1)^3)',
expected: '1+243*x^2*y^2+27*x*y+729*x^3*y^3',
expectedValue: '254478.51475300005',
},
{
given: 'expand(x*(x+1))',
expected: 'x+x^2',
expectedValue: '6.51',
},
{
given: 'expand(x*(x+1)^5)',
expected: '10*x^3+10*x^4+5*x^2+5*x^5+x+x^6',
expectedValue: '601.212171',
},
{
given: 'expand((x*y)^x+(x*y)^2)',
expected: '(x*y)^x+x^2*y^2',
expectedValue: '106.3076174497575',
},
{
given: 'expand((3*x+4*y)^4)',
expected: '256*y^4+432*x^3*y+768*x*y^3+81*x^4+864*x^2*y^2',
expectedValue: '144590.0625',
},
{
given: 'expand((9*y*x+1)^2)',
expected: '1+18*x*y+81*x^2*y^2',
expectedValue: '4015.7568999999994',
},
{
given: 'expand((x+5)*(x-3)-x^2)',
expected: '-15+2*x',
expectedValue: '-10.8',
},
{
given: 'expand(((x^3+x)^x*(x^2+x)^x+1)*x)',
expected: '(x+x^2)^x*(x+x^3)^x*x+x',
expectedValue: '17667.12052556627',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate(values).text('decimals');
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(round(value, 12)).toEqual(round(testCases[i].expectedValue, 12));
}
});
it('should expand correctly', () => {
expect(nerdamer('expand((a^2*b*c)^(-1))').toString()).toEqual('a^(-2)*b^(-1)*c^(-1)');
expect(nerdamer('expand(((a^2*b)(x+1))^2)').toString()).toEqual('2*a^4*b^2*x+a^4*b^2+a^4*b^2*x^2');
expect(nerdamer('expand(5*x/(c+d)^2)').toString()).toEqual('5*(2*c*d+c^2+d^2)^(-1)*x');
expect(nerdamer('expand((a+b)*(c+d))').toString()).toEqual('a*c+a*d+b*c+b*d');
expect(nerdamer('expand(5*(a+b)*(c+d))').toString()).toEqual('5*a*c+5*a*d+5*b*c+5*b*d');
expect(nerdamer('expand(4*parens(x+1)^2)').toString()).toEqual('4+4*x^2+8*x');
expect(nerdamer('expand(4*(a*b)*(c*b))').toString()).toEqual('4*a*b^2*c');
expect(nerdamer('expand(4*(a*b)*(c*b)+1)').toString()).toEqual('1+4*a*b^2*c');
expect(nerdamer('expand(3*(a+b)*(g+i)*(x*k))').toString()).toEqual('3*a*g*k*x+3*a*i*k*x+3*b*g*k*x+3*b*i*k*x');
expect(nerdamer('expand(2*x*(x+1)^3)').toString()).toEqual('2*x+2*x^4+6*x^2+6*x^3');
expect(nerdamer('expand((2*(a*b)*(x+1)^3)^2)').toString()).toEqual('24*a^2*b^2*x+24*a^2*b^2*x^5+4*a^2*b^2+4*a^2*b^2*x^6+60*a^2*b^2*x^2+60*a^2*b^2*x^4+80*a^2*b^2*x^3');
expect(nerdamer('expand((2*(a*b)*(x+1)^3)^2+1)').toString()).toEqual('1+24*a^2*b^2*x+24*a^2*b^2*x^5+4*a^2*b^2+4*a^2*b^2*x^6+60*a^2*b^2*x^2+60*a^2*b^2*x^4+80*a^2*b^2*x^3');
expect(nerdamer('expand((d/(x+1)+1)^2)').toString()).toEqual('(1+2*x+x^2)^(-1)*d^2+1+2*(1+x)^(-1)*d');
expect(nerdamer('expand(2*cos((d/(x+1)+1)^2)^2)').toString()).toEqual('2*cos((1+2*x+x^2)^(-1)*d^2+1+2*(1+x)^(-1)*d)^2');
});
it('should handle imaginary log arguments', () => {
// Given
const testCases = [
{
given: 'log(5*i)',
expected: '1.5707963267948966*i+1.60943791243410026',
},
{
given: 'log(8+5*i)',
expected: '0.5585993153435624*i+2.24431818486606994',
},
{
given: 'log(123-2*i)',
expected: '-0.01625872980512959*i+4.81231653434351391',
},
{
given: 'log(123-2*i+a)',
expected: 'log(-2*i+123+a)',
},
/*
{
given: 'log(123-2*i+a)',
expected: '-atan2(123+a,-2)*i+1.5707963267948966*i+log(sqrt((123+a)^2+4))'
},
{
given: 'log(x+2*i)',
expected: '-atan2(x,2)*i+1.5707963267948966*i+log(sqrt(4+x^2))'
}
*/
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate().text('decimals', 17);
// Then
expect(value).toEqual(testCases[i].expected);
}
});
it('should convert from polar to rectangular', () => {
// Given
const testCases = [
{
given: 'rectform(sqrt(34)*e^(i*atan(3/5)))',
expected: '3*i+5',
},
// PENDING:
// (-1)^(1/4)*sqrt(2)
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate().text('decimals');
// Then
expect(value).toEqual(testCases[i].expected);
}
});
it('should convert from rectangular to polar', () => {
// Given
const testCases = [
{
given: 'polarform(3*i+5)',
expected: 'e^(atan(3/5)*i)*sqrt(34)',
},
{
given: 'polarform(i-1)',
expected: '(i*sqrt(2)^(-1)+sqrt(2)^(-1))*sqrt(2)',
},
{
given: 'polarform(i+1)',
expected: 'e^(atan(1)*i)*sqrt(2)',
},
{
given: 'polarform(a*i+b*1)',
expected: 'e^(atan(a*b^(-1))*i)*sqrt(a^2+b^2)',
},
{
given: 'polarform(3)',
expected: '3',
},
{
given: 'polarform(i)',
expected: 'i',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.toString();
// Then
expect(value).toEqual(testCases[i].expected);
}
});
it('should compute powers', () => {
// Given
const testCases = [
{
given: '0^(1/2)',
expected: '0',
expectedValue: '0',
},
{
given: '2^(1/2)',
expected: 'sqrt(2)',
expectedValue: '1.4142135623730951',
},
{
given: '3/4/-5/7',
expected: '-3/140',
expectedValue: '-0.02142857142857143',
},
{
given: '(2/5)^(1/2)',
expected: 'sqrt(2)*sqrt(5)^(-1)',
expectedValue: '0.6324555320336757',
},
{
given: '2^(1/2)+sqrt(2)',
expected: '2*sqrt(2)',
expectedValue: '2.8284271247461903',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate(values).text('decimals');
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(round(value), 14).toEqual(round(testCases[i].expectedValue), 14);
}
});
it('should handle large exponents', () => {
expect(nerdamer('((0.06/3650))^(365)').toString()).toEqual(
'1410126170338158616048224728371571380367482072097134683678107761' +
'52508851857709566371341862240808591002757985609412198322971415396168976254349069' +
'9072942506404339112922628405843/22958690276909851695578696288325771692413979062611' +
'991343897836236337139161946070457727457304403772796893304635803331160164612018173513' +
'31134891165302696107379996154818718135122744754040421370203240041207474864695474953483' +
'0902803016857824896441156744745927014013655670039876606817604403974679132586661890547851' +
'679296531769750182377028054935351721036749094036432130424970214049875323757624442119121774' +
'055322139084444881193088085062373618050647934648517067336395372063877340185058649456212635239' +
'066781525382959311599987961299501210179615619443916881386730704082805297329446411862362398493471' +
'826680176950800344487654991092708947796616392537184126525313140738179830882678244141981560034679553' +
'975783364407913480224860273677558689624530555494789243375386735721249700734052057353034685422151396642' +
'918026922556798918041766876185384884777704756416389503981017717926985777672007862927270802670740330468020' +
'802233942408161027975787463864142754167881620056649731281986417206144622511945116440478852141832742043753412' +
'722079707428689254358366463206869962206219178342509475906640460010907598207195708896768921596249362559252745779' +
'251530796907715219008462775395896084216357246887696419435087591409683227539062500000000000000000000000000000000000' +
'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
'00000000000000000000000000000000000000000000000000000000000000000000000000000000'
);
expect(nerdamer('1000*(1+(0.06/365))^(365*3)').evaluate().text()).toEqual('1197.1996529367538872360');
});
it('should compute factorials', () => {
// Given
const testCases = [
{
given: '(1+x^2)!',
expected: 'factorial(1+x^2)',
expectedValue: '245.1516183677083',
},
{
given: '10!',
expected: 'factorial(10)',
expectedValue: '3628800',
},
{
given: 'x!',
expected: 'factorial(x)',
expectedValue: '2.197620278392476',
},
{
given: 'x!*x!',
expected: 'factorial(x)^2',
expectedValue: '4.829534888001823',
},
{
given: '3*(1+x!*x!)',
expected: '3*(1+factorial(x)^2)',
expectedValue: '17.488604664005468',
},
{
given: '3*(1+x!*x!)!',
expected: '3*factorial(1+factorial(x)^2)',
expectedValue: '1573.2041488172601',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate(values).text('decimals');
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(round(value), 14).toEqual(round(testCases[i].expectedValue), 14);
}
});
it('should compute symbolic factorials', () => {
// Given
const testCases = [
{
given: '(-1/2)!',
expected: 'sqrt(pi)',
},
{
given: '(-7/2)!',
expected: '(-8/15)*sqrt(pi)',
},
{
given: '(-9/2)!',
expected: '(16/105)*sqrt(pi)',
},
{
given: '(9/2)!',
expected: '(945/32)*sqrt(pi)',
},
{
given: '(7/2)!',
expected: '(105/16)*sqrt(pi)',
},
{
given: '(1/2)!',
expected: '(1/2)*sqrt(pi)',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
}
});
it('should handle square roots', () => {
// Given
const testCases = [
{
given: 'sqrt(1+x)^(4*x)',
expected: '(1+x)^(2*x)',
expectedValue: '115.80281433592612',
},
{
given: 'sqrt(2*sqrt(5))',
expected: '5^(1/4)*sqrt(2)',
expectedValue: '2.114742526881128',
},
{
given: 'sqrt(2)*sqrt(2)',
expected: '2',
expectedValue: '2',
},
{
given: 'sqrt(1/2)',
expected: 'sqrt(2)^(-1)',
expectedValue: '0.7071067811865475',
},
{
given: 'sqrt(2)^2',
expected: '2',
expectedValue: '2',
},
{
given: '6*sqrt(2)^4',
expected: '24',
expectedValue: '24',
},
{
given: 'sqrt(x^2)*sqrt(x^4)',
expected: 'abs(x)*x^2',
expectedValue: '9.261',
},
{
given: 'sqrt((5/2)*x^10)',
expected: 'abs(x)*sqrt(2)^(-1)*sqrt(5)*x^4',
expectedValue: '64.5753067708567',
},
{
given: '(sqrt((5/2)*x^10))*-sqrt(2)',
expected: '-abs(x)*sqrt(5)*x^4',
expectedValue: '-91.3232746297487',
},
{
given: 'sqrt(9)',
expected: '3',
expectedValue: '3',
},
{
given: 'sqrt(-9)',
expected: '3*i',
expectedValue: '3*i',
},
{
given: 'sqrt(a/x)',
expected: 'sqrt(a)*sqrt(x)^(-1)',
expectedValue: '1.879716290649558',
},
{
given: 'sqrt(-x)',
expected: 'sqrt(-x)',
expectedValue: '1.4491376746189437*i',
},
{
given: 'sqrt(-x)*sqrt(-x)',
expected: '-x',
expectedValue: '-2.1',
},
{
given: 'sqrt(-x)*sqrt(-x)+4*x',
expected: '3*x',
expectedValue: '6.3',
},
{
given: 'sqrt((1/2*x)^(1/2))',
expected: '2^(-1/4)*x^(1/4)',
expectedValue: '1.0122722344290394',
},
{
given: 'sqrt(4*sqrt(2)^(-1)*x^(1/2))',
expected: '2^(3/4)*x^(1/4)',
expectedValue: '2.0245444688580787',
},
{
given: 'sqrt(4+x)',
expected: 'sqrt(4+x)',
expectedValue: '2.4698178070456938',
},
{
given: '(1/2)*x^(1/2)+sqrt(x)',
expected: '(3/2)*sqrt(x)',
expectedValue: '2.173706511928416',
},
{
given: 'sqrt((4+x)^2)',
expected: 'abs(4+x)',
expectedValue: '6.1',
},
{
given: '2*sqrt(3/4)',
expected: 'sqrt(3)',
expectedValue: '1.7320508075688772',
},
{
given: 'sqrt(2)*sqrt(8)',
expected: '4',
expectedValue: '4',
},
{
given: 'sqrt(242)',
expected: '11*sqrt(2)',
expectedValue: '15.556349186104047',
},
{
given: 'sqrt(4)^-1',
expected: '1/2',
expectedValue: '0.5',
},
{
given: 'sqrt(4*x^2)',
expected: '2*abs(x)',
expectedValue: '4.2',
},
{
given: '74689676.31109099*sqrt(5578547747455547)',
expected: '(824947474856/11045)*sqrt(5578547747455547)',
expectedValue: '5578547747455547',
},
{
given: 'sqrt(2/x)',
expected: 'sqrt(2)*sqrt(x)^(-1)',
expectedValue: '0.9759000729485331',
},
{
given: 'sqrt(3^x)',
expected: 'sqrt(3^x)',
expectedValue: '3.169401925649',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate(values).text('decimals');
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(round(value, 12)).toEqual(round(testCases[i].expectedValue, 12));
}
});
it('should simplify square roots', () => {
expect(nerdamer('sqrt(128/49)').toString()).toEqual('(8/7)*sqrt(2)');
expect(nerdamer('sqrt(2)-2/(sqrt(2))').toString()).toEqual('0');
expect(nerdamer('expand((sqrt(7)+3sqrt(2))(sqrt(7)-3sqrt(2)))').toString()).toEqual('-11');
expect(nerdamer('3sqrt(2)*2sqrt(6)').toString()).toEqual('12*sqrt(3)');
});
it('should handle square roots of negative values', () => {
expect(nerdamer('sqrt(-x)').evaluate().text()).toEqual('sqrt(-x)');
expect(nerdamer('sqrt(-0.5*x)').evaluate().text()).toEqual('0.7071067811865475*sqrt(-x)');
expect(nerdamer('sqrt(-4)').evaluate().text()).toEqual('2*i');
expect(nerdamer('sqrt(-pi)').evaluate().text()).toEqual('1.772453850905516*i');
});
it('should expand square roots', () => {
// Given
const testCases = [
{
given: '(sqrt(7)+3sqrt(2))*(sqrt(7)-3sqrt(2))',
expected: '-11',
},
{
given: 'sqrt(33)*sqrt(11)',
expected: '11*sqrt(3)',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given).expand();
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
}
});
it('should correctly test for squareness', () => {
expect(nerdamer('16x^2*y^2').symbol.isSquare()).toBe(true);
expect(nerdamer('16x^2*y^2-1').symbol.isSquare()).toBe(false);
expect(nerdamer('9').symbol.isSquare()).toBe(true);
expect(nerdamer('(5+x)^6').symbol.isSquare()).toBe(true);
expect(nerdamer('(x+y)^2').symbol.isSquare()).toBe(true);
expect(nerdamer('9^(1/4)').symbol.isSquare()).toBe(false);
expect(nerdamer('x^(1/2)').symbol.isSquare()).toBe(false);
});
it('should correctly test for cubeness', () => {
expect(nerdamer('64x^3*y^3').symbol.isCube()).toBe(true);
expect(nerdamer('64x^3*y^3-1').symbol.isCube()).toBe(false);
expect(nerdamer('7').symbol.isCube()).toBe(false);
expect(nerdamer('27').symbol.isCube()).toBe(true);
expect(nerdamer('(5+x)^6').symbol.isCube()).toBe(true);
expect(nerdamer('(x+y)^2').symbol.isCube()).toBe(false);
expect(nerdamer('9^(1/4)').symbol.isCube()).toBe(false);
expect(nerdamer('x^(1/2)').symbol.isCube()).toBe(false);
expect(nerdamer('216*z^6').symbol.isCube()).toBe(true);
});
it('should support the imaginary number i', () => {
// Given
const testCases = [
{
given: 'i*i',
expected: '-1',
expectedValue: '-1',
},
{
given: 'i*8*i',
expected: '-8',
expectedValue: '-8',
},
{
given: 'i^(2/3)',
expected: '-1',
expectedValue: '-1',
},
{
given: '(256*i)^(1/8)',
expected: '2*(-1)^(1/16)',
expectedValue: '0.3901806440322565*i+1.96157056080646092',
},
{
given: 'i/i',
expected: '1',
expectedValue: '1',
},
{
given: '(1/i)*i',
expected: '1',
expectedValue: '1',
},
{
given: 'i^(-1)',
expected: '-i',
expectedValue: '-i',
},
{
given: 'i^(2)',
expected: '-1',
expectedValue: '-1',
},
{
given: 'i^(-2)',
expected: '-1',
expectedValue: '-1',
},
{
given: 'i^(-1)',
expected: '-i',
expectedValue: '-i',
},
// Euler's identity
{
given: 'e^(i*pi)+e^(2*i*pi)',
expected: '0',
expectedValue: '0',
},
{
given: 'exp(i + x pi)',
expected: 'e^(i+pi*x)',
expectedValue: '396.12035908272455345+616.9209071285088*i',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate(values).text('decimals', 17);
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(value).toEqual(testCases[i].expectedValue);
}
});
it('should handle powers with results using i', () => {
// Given
const testCases = [
{
given: '(-2/3*x)^x',
expected: '(-x)^x*2^x*3^(-x)',
// TODO: Evaluates to NaN somewhere
expectedValue: '2.0270706004935857*(-1)^2.1',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate(values).text('decimals');
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(value).toEqual(testCases[i].expectedValue);
}
});
it('should compute logarithms correctly', () => {
// Given
const testCases = [
{
given: 'log(e)',
expected: '1',
expectedValue: '1',
},
{
given: 'log(e^e)',
expected: 'e',
expectedValue: '2.718281828459045',
},
{
given: 'log(1/e^e)',
expected: '-e',
expectedValue: '-2.718281828459045',
},
{
given: 'log(1/sqrt(2))',
expected: '(-1/2)*log(2)',
expectedValue: '-0.34657359027997',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate().text('decimals');
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
expect(round(value, 14)).toEqual(round(testCases[i].expectedValue), 14);
}
// Np issue #2
expect(nerdamer('log(2*(a+b)^7)').toString()).toEqual('log(2*(a+b)^7)');
});
it('should check for equality', () => {
const a = nerdamer('2sqrt(5)');
const b = nerdamer('2sqrt(5)');
const c = nerdamer('sqrt(13)');
const d = nerdamer('sqrt(21)');
const e = nerdamer('sqrt(20)');
const f = nerdamer('sqrt(5) + sqrt(3)');
const g = nerdamer('sqrt(5) + sqrt(7)');
expect(a.lt(g)).toBe(true);
expect(a.lt(f)).toBe(false);
expect(a.gt(c)).toBe(true);
expect(a.gt(d)).toBe(false);
expect(a.gt(e)).toBe(false);
expect(a.lt(e)).toBe(false);
expect(a.eq(e)).toBe(true);
expect(b.gte(a)).toBe(true);
expect(c.gte(a)).toBe(false);
expect(e.lte(d)).toBe(true);
expect(f.lte(g)).toBe(true);
});
/** Based on commit cf8c0f8. */
it('should not cause infinite recursion', () => {
// Given
const formula = '1/(1+x)+(1+x)';
// When
const parsed = nerdamer(formula);
const result = parsed.evaluate().toString();
// Then
expect(result).toBe('(1+x)^(-1)+1+x');
});
it('should support ceil and floor', () => {
// Given
const testCases = [
{
given: 'floor(204)',
expected: '204',
},
{
given: 'floor(10.893)',
expected: '10',
},
{
given: 'floor(-10.3)',
expected: '-11',
},
{
given: 'ceil(204)',
expected: '204',
},
{
given: 'ceil(10.893)',
expected: '11',
},
{
given: 'ceil(-10.9)',
expected: '-10',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate().text('decimals');
// Then
expect(value).toEqual(testCases[i].expected);
}
});
it('should round', () => {
// Given
const testCases = [
{
given: 'round(204)',
expected: '204',
},
{
given: 'round(10.893)',
expected: '11',
},
{
given: 'round(10.893, 1)',
expected: '10.9',
},
{
given: 'round(-10.3)',
expected: '-10',
},
{
given: 'round(204)',
expected: '204',
},
{
given: 'round(10.1)',
expected: '10',
},
{
given: 'round(1.23423534e-12,-2)',
expected: '0.00000000000123',
},
{
given: 'round(1.23423534e12,-2)',
expected: '1230000000000',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate().text('decimals');
// Then
expect(value).toEqual(testCases[i].expected);
}
});
it('should support trunc()', () => {
// Given
const testCases = [
{
given: 'trunc(0)',
expected: '0',
},
{
given: 'trunc(10.234)',
expected: '10',
},
{
given: 'trunc(-9.99)',
expected: '-9',
},
{
given: 'trunc(0.99)',
expected: '0',
},
{
given: 'trunc(-0.7555)',
expected: '0',
},
{
given: 'trunc(8.9 * -4.9)',
expected: '-43',
},
{
given: 'trunc(8.9) * trunc(-4.9)',
expected: '-32',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate().text('decimals');
// Then
expect(value).toEqual(testCases[i].expected);
}
});
it('should support continued fractions', () => {
// Given
const testCases = [
{
given: 'continuedFraction(2.145474, 11)',
expected: '[1,2,[6,1,6,1,16,8,2,1,3,2]]',
},
{
given: 'continuedFraction(-6/7)',
expected: '[-1,0,[1,6]]',
},
{
given: 'continuedFraction(sqrt(2), 5)',
expected: '[1,1,[2,2,2,2,2]]',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
// Then
expect(parsed.toString()).toEqual(testCases[i].expected);
}
});
/** #35 #76: Support multiple minus signs and brackets */
it('should support prefix operator with parantheses', () => {
// Given
const testCases = [
{
given: '(a+x)--(x+a)',
expected: '2*a+2*x',
},
{
given: '(3)---(3)',
expected: '0',
},
{
given: '-(1)--(1-1--1)',
expected: '0',
},
{
given: '-(-(1))-(--1)',
expected: '0',
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const parsed = nerdamer(testCases[i].given);
const value = parsed.evaluate().text('decimals');
// Then
expect(value).toEqual(testCases[i].expected);
}
});
// #78
it('should substitute variables', () => {
// Given
const testCases = [
{
given: '2*(x+1)^2',
sub: 'x+1',
sub_with: 'u',
expected: '2*u^2',
},
{
given: '2*(x+1+a)^2',
sub: 'x+1',
sub_with: 'u',
expected: '2*(1+a+x)^2',
},
{
given: '2*(x+1)^(x+1)',
sub: 'x+1',
sub_with: 'u',
expected: '2*u^u',
},
{
given: '2*(x+1)^2',
sub: '(x+1)^2',
sub_with: 'u^x',
expected: '2*u^x',
},
{
given: '2*(x+1)^2',
sub: 'x+1',
sub_with: 'u^x',
expected: '2*u^(2*x)',
},
{
given: '(2*(x+1)^2+a)^2',
sub: '(x+1)^2',
sub_with: 'u^x',
expected: '(2*u^x+a)^2',
},
{
given: '(x^x+y)^(x^x-t)',
sub: 'x^x',
sub_with: '4',
expected: '(4+y)^(-t+4)',
},
{
given: '(cos(x)+cos(x)^2)',
sub: 'cos(x)',
sub_with: '4',
expected: '20',
},
{
given: '(cos(x)+cos(x)^2)',
sub: '(cos(x)+cos(x)^2)',
sub_with: '4',
expected: '4',
},
{
given: '(cos(x)+cos(x^6)^2)',
sub: '(cos(x)+cos(x)^2)',
sub_with: '4',
expected: 'cos(x)+cos(x^6)^2',
},
{
given: '(cos(x)+cos(x^6)^2)',
sub: 'cos(x)',
sub_with: '4',
expected: '4+cos(x^6)^2',
},
{
given: '(cos(x)+cos(x^6)^2)',
sub: '2',
sub_with: '4',
expected: 'error',
},
];
for (let i = 0; i < testCases.length; ++i) {
const testCase = testCases[i];
let parsed;
// When
try {
parsed = nerdamer(testCase.given).sub(testCase.sub, testCase.sub_with).toString();
} catch (e) {
parsed = 'error';
}
// Then
expect(parsed).toEqual(testCases[i].expected);
}
});
it('should ignore constants and special values', () => {
const nerdamerCore = nerdamer.getCore();
expect(nerdamer('e').variables()).toEqual([]);
expect(nerdamer('pi').variables()).toEqual([]);
expect(nerdamer(nerdamerCore.Settings.IMAGINARY).variables()).toEqual([]);
});
/** #44: a+b - (a+b) not evaluated as 0 */
it('should perform subtraction of terms', () => {
// Given
const formula = 'a+b - (a+b)';
// When
const result = nerdamer(formula).toString();
// Then
expect(result).toBe('0');
});
/** #46: (x^(1/2)*x^(1/3))-x^(5/6) should be 0 */
it('should result in 0', () => {
// Given
const formula = '(x^(1/2)*x^(1/3))-x^(5/6)';
// When
const result = nerdamer(formula).toString();
// Then
expect(result).toBe('0');
});
/** #47: (a^2)/(a*b) should be a/b */
it('should simplify correctly', () => {
// Given
const formula = '(a^2)/(a*b)';
// When
const result = nerdamer(formula).toString();
// Then
// TODO jiggzson: Result is correct but a/b would be simpler
expect(result).toBe('a*b^(-1)');
});
/** #56: x/sqrt(x) = x^(3/2) */
it('should calculate x/sqrt(x) correctly', () => {
// Given
const formula = 'x/sqrt(x)';
// When
const result = nerdamer(formula).toString();
// Then
expect(result).toBe('x^(1/2)');
});
/** #60: sin(x) looks like sin(abs(x)) */
it('should respect the sign of argument for sin(x)', () => {
// Given
const halfSqrt2 = '0.7071067811865475'; // Sqrt(2)/2
const halfSqrt3 = '0.8660254037844385'; // Sqrt(3)/2
const testCases = [
{
given: '-pi',
expected: '0',
},
{
given: '-3/4*pi',
expected: `-${halfSqrt2}`,
},
{
given: '-2/3*pi',
expected: `-${halfSqrt3}`,
},
{
given: '-1/2*pi',
expected: '-1',
},
{
given: '-1/6*pi',
expected: '-0.5',
},
{
given: '0',
expected: '0',
},
{
given: '1/4*pi',
expected: halfSqrt2,
},
{
given: '1/2*pi',
expected: '1',
},
{
given: '3/4*pi',
expected: halfSqrt2,
},
{
given: 'pi',
expected: '0',
},
{
given: '3/2*pi',
expected: '-1',
},
{
given: '2*pi',
expected: '0',
},
{
given: '2.25 * pi',
expected: halfSqrt2,
},
];
for (let i = 0; i < testCases.length; ++i) {
// When
const result = nerdamer(`sin(${testCases[i].given})`).evaluate().text('decimals');
// Then
expect(round(result, 14)).toEqual(round(testCases[i].expected), 14);
}
});
it('should compute complex numbers', () => {
const testCases = [
// SYMBOLIC
{
given: 'cos(3*i+a)',
expected: 'cos(3*i+a)',
},
{
given: 'sin(3*i+a)',
expected: 'sin(3*i+a)',
},
{
given: 'tan(3*i+a)