Skip to content

Commit

Permalink
OperatorNode: Support more than two operands in toTex/String
Browse files Browse the repository at this point in the history
Only for multiplication and addition though.
  • Loading branch information
FSMaxB committed Nov 13, 2016
1 parent f4ef422 commit 493bd7f
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 2 deletions.
58 changes: 56 additions & 2 deletions lib/expression/node/OperatorNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ function factory (type, config, load, typed, math) {
var precedence = operators.getPrecedence(root, parenthesis);
var associativity = operators.getAssociativity(root, parenthesis);

if ((parenthesis === 'all') || (args.length > 2)) {
if ((parenthesis === 'all') || ((args.length > 2) && (root.getIdentifier() !== 'OperatorNode:add') && (root.getIdentifier() !== 'OperatorNode:multiply'))) {
var parens = [];
args.forEach(function (arg) {
switch (arg.getContent().type) { //Nodes that don't need extra parentheses
Expand All @@ -140,7 +140,7 @@ function factory (type, config, load, typed, math) {
}

if (args.length === 0) {
return [];
return [];
} else if (args.length === 1) { //unary operators
//precedence of the operand
var operandPrecedence = operators.getPrecedence(args[0], parenthesis);
Expand Down Expand Up @@ -269,6 +269,29 @@ function factory (type, config, load, typed, math) {
}

return [lhsParens, rhsParens];
} else if ((args.length > 2) && ((root.getIdentifier() === 'OperatorNode:add') || (root.getIdentifier() === 'OperatorNode:multiply'))) {
var parensArray = [];

args.forEach(function (arg) {
var parens;
var argPrecedence = operators.getPrecedence(arg, parenthesis);
var assocWithArg = operators.isAssociativeWith(root, arg, parenthesis);
var argAssociativity = operators.getAssociativity(arg, parenthesis);
if (argPrecedence === null) {
//if the argument has no defined precedence, no parens are needed
parens = false;
} else if ((precedence === argPrecedence) && (associativity === argAssociativity) && !assocWithArg) {
parens = true;
} else if (argPrecedence < precedence) {
parens = true;
} else {
parens = false;
}

parensArray.push(parens);
});

return parensArray;
}
}

Expand Down Expand Up @@ -315,6 +338,22 @@ function factory (type, config, load, typed, math) {
}

return lhs + ' ' + this.op + ' ' + rhs;
} else if ((args.length > 2) && ((this.getIdentifier() === 'OperatorNode:add') || (this.getIdentifier() === 'OperatorNode:multiply'))) {
var stringifiedArgs = [];
args.forEach(function (arg, index) {
arg = arg.toString(options);
if (parens[index]) { //put in parenthesis?
arg = '(' + arg + ')';
}

stringifiedArgs.push(arg);
});

if (this.implicit && (this.getIdentifier() === 'OperatorNode:multiply') && (implicit === 'hide')) {
return stringifiedArgs.join(' ');
}

return stringifiedArgs.join(' ' + this.op + ' ');
} else {
//fallback to formatting as a function call
return this.fn + '(' + this.args.join(', ') + ')';
Expand Down Expand Up @@ -391,6 +430,21 @@ function factory (type, config, load, typed, math) {
}
}
return lhsTex + op + rhsTex;
} else if ((args.length > 2) && ((this.getIdentifier() === 'OperatorNode:add') || (this.getIdentifier() === 'OperatorNode:multiply'))) {
var texifiedArgs = [];
args.forEach(function (arg, index) {
arg = arg.toTex(options);
if (parens[index]) {
arg = '\\left(' + arg + '\\right)';
}
texifiedArgs.push(arg);
});

if ((this.getIdentifier() === 'OperatorNode:multiply') && this.implicit) {
return texifiedArgs.join('~');
}

return texifiedArgs.join(op)
} else {
//fall back to formatting as a function call
//as this is a fallback, it doesn't use
Expand Down
109 changes: 109 additions & 0 deletions test/expression/node/OperatorNode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,61 @@ describe('OperatorNode', function() {

});

it ('should stringify addition and multiplication with more than two operands', function () {
var a = new SymbolNode('a');
var b = new SymbolNode('b');
var c = new SymbolNode('c');

var add = new OperatorNode('+', 'add', [a, b, c]);
var multiply = new OperatorNode('*', 'multiply', [a, b, c]);
var implicitMultiply = new OperatorNode('*', 'multiply', [a, b, c], true);

assert.equal(add.toString(), 'a + b + c');
assert.equal(multiply.toString(), 'a * b * c');
assert.equal(implicitMultiply.toString(), 'a b c');
});

it ('should stringify addition and multiplication with more than two operands including OperatorNode', function () {
var a = new SymbolNode('a');
var b = new SymbolNode('b');
var c = new SymbolNode('c');
var d = new SymbolNode('d');

var mult = new OperatorNode('*', 'multiply', [a,b]);
var add = new OperatorNode('+', 'add', [a, b]);

var multipleMultWithMult = new OperatorNode('*', 'multiply', [c, mult, d]);
var multipleMultWithAdd = new OperatorNode('*', 'multiply', [c, add, d]);
var multipleAddWithMult = new OperatorNode('+', 'add', [c, mult, d]);
var multipleAddWithAdd = new OperatorNode('+', 'add', [c, add, d]);

assert.equal(multipleMultWithMult.toString(), 'c * a * b * d');
assert.equal(multipleMultWithAdd.toString(), 'c * (a + b) * d');
assert.equal(multipleAddWithMult.toString(), 'c + a * b + d');
assert.equal(multipleAddWithAdd.toString(), 'c + a + b + d');
});

it ('should stringify an OperatorNode that contains an operatornode with more than two operands', function () {
var a = new SymbolNode('a');
var b = new SymbolNode('b');
var c = new SymbolNode('c');
var d = new SymbolNode('d');

var mult = new OperatorNode('*', 'multiply', [a, b, c]);
var add = new OperatorNode('+', 'add', [a, b, c]);

var addWithMult = new OperatorNode('+', 'add', [mult, d]);
var addWithAdd = new OperatorNode('+', 'add', [add, d]);
var multWithMult = new OperatorNode('*', 'multiply', [mult, d]);
var multWithAdd = new OperatorNode('*', 'multiply', [add, d]);

assert.equal(addWithMult.toString(), 'a * b * c + d');
assert.equal(addWithAdd.toString(), 'a + b + c + d');
assert.equal(multWithMult.toString(), 'a * b * c * d');
assert.equal(multWithAdd.toString(), '(a + b + c) * d');

});

it ('should stringify an OperatorNode with nested operator nodes', function () {
var a = new ConstantNode(2);
var b = new ConstantNode(3);
Expand Down Expand Up @@ -447,6 +502,60 @@ describe('OperatorNode', function() {
assert.equal(m3.toTex(), '\\left(2+3\\right)\\cdot4-5');
});

it ('should LaTeX addition and multiplication with more than two operands', function () {
var a = new SymbolNode('a');
var b = new SymbolNode('b');
var c = new SymbolNode('c');

var add = new OperatorNode('+', 'add', [a, b, c]);
var multiply = new OperatorNode('*', 'multiply', [a, b, c]);
var implicitMultiply = new OperatorNode('*', 'multiply', [a, b, c], true);

assert.equal(add.toTex(), ' a+\\mathrm{b}+ c');
assert.equal(multiply.toTex(), ' a\\cdot\\mathrm{b}\\cdot c');
assert.equal(implicitMultiply.toTex(), ' a~\\mathrm{b}~ c');
});

it ('should LaTeX addition and multiplication with more than two operands including OperatorNode', function () {
var a = new SymbolNode('a');
var b = new SymbolNode('b');
var c = new SymbolNode('c');
var d = new SymbolNode('d');

var mult = new OperatorNode('*', 'multiply', [a,b]);
var add = new OperatorNode('+', 'add', [a, b]);

var multipleMultWithMult = new OperatorNode('*', 'multiply', [c, mult, d]);
var multipleMultWithAdd = new OperatorNode('*', 'multiply', [c, add, d]);
var multipleAddWithMult = new OperatorNode('+', 'add', [c, mult, d]);
var multipleAddWithAdd = new OperatorNode('+', 'add', [c, add, d]);

assert.equal(multipleMultWithMult.toTex(), ' c\\cdot a\\cdot\\mathrm{b}\\cdot d');
assert.equal(multipleMultWithAdd.toTex(), ' c\\cdot\\left( a+\\mathrm{b}\\right)\\cdot d');
assert.equal(multipleAddWithMult.toTex(), ' c+ a\\cdot\\mathrm{b}+ d');
assert.equal(multipleAddWithAdd.toTex(), ' c+ a+\\mathrm{b}+ d');
});

it ('should LaTeX an OperatorNode that contains an operatornode with more than two operands', function () {
var a = new SymbolNode('a');
var b = new SymbolNode('b');
var c = new SymbolNode('c');
var d = new SymbolNode('d');

var mult = new OperatorNode('*', 'multiply', [a, b, c]);
var add = new OperatorNode('+', 'add', [a, b, c]);

var addWithMult = new OperatorNode('+', 'add', [mult, d]);
var addWithAdd = new OperatorNode('+', 'add', [add, d]);
var multWithMult = new OperatorNode('*', 'multiply', [mult, d]);
var multWithAdd = new OperatorNode('*', 'multiply', [add, d]);

assert.equal(addWithMult.toTex(), ' a\\cdot\\mathrm{b}\\cdot c+ d');
assert.equal(addWithAdd.toTex(), ' a+\\mathrm{b}+ c+ d');
assert.equal(multWithMult.toTex(), ' a\\cdot\\mathrm{b}\\cdot c\\cdot d');
assert.equal(multWithAdd.toTex(), '\\left( a+\\mathrm{b}+ c\\right)\\cdot d');
});

it('should LaTeX fractions with operators that are enclosed in parenthesis', function () {
var a = new ConstantNode(1);
var b = new ConstantNode(2);
Expand Down

0 comments on commit 493bd7f

Please sign in to comment.