lib/util: Move over from callbacks to templates

This commit is contained in:
Max Bruckner 2015-04-12 23:44:26 +02:00
parent 8b5ed85875
commit 240eeecccd
2 changed files with 124 additions and 337 deletions

View File

@ -40,6 +40,41 @@ exports.symbols = {
'undefined': '\\mathbf{?}'
};
exports.operators = {
'transpose': '^{\\top}',
'factorial': '!',
'pow': '^',
'dotPow': '.^{\\wedge}', //TODO find ideal solution
'unaryPlus': '+',
'unaryMinus': '-',
'bitNot': '~', //TODO find ideal solution
'not': '\\neg',
'multiply': '\\cdot',
'divide': '\\frac', //TODO how to handle that properly?
'dotMultiply': '.\\cdot', //TODO find ideal solution
'dotDivide': '.:', //TODO find ideal solution
'mod': '\\mod',
'add': '+',
'subtract': '-',
'to': '\\rightarrow',
'leftShift': '<<',
'rightArithShift': '>>',
'rightLogShift': '>>>',
'equal': '=',
'unequal': '\\neq',
'smaller': '<',
'larger': '>',
'smallerEq': '\\leq',
'largerEq': '\\geq',
'bitAnd': '\\&',
'bitXor': '\\underline{|}',
'bitOr': '|',
'and': '\\wedge',
'xor': '\\veebar',
'or': '\\vee'
};
//create a comma separated list of function arguments
function functionArgs(args, callbacks) {
return args.map( function (arg) {
@ -79,71 +114,30 @@ function expandTemplate(template, name, args) {
//this is a map containing all the latex converters for all the functions
var functions = {
//arithmetic
'abs': function (node, callbacks) {
return '\\left|{' + node.args[0].toTex(callbacks) + '}\\right|';
},
'add': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['add'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'ceil': function (node, callbacks) {
return '\\left\\lceil{' + node.args[0].toTex(callbacks) + '}\\right\\rceil';
},
'cube': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks) + '}\\right)^{3}';
},
'divide': function (node, callbacks) {
return '\\frac{' + node.args[0].toTex(callbacks)
+ '}{' + node.args[1].toTex(callbacks) + '}';
},
'dotDivide': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['dotDivide'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'dotMultiply': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['dotMultiply'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'dotPow': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['dotPow'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'exp': function (node, callbacks) {
return '\\exp\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'abs': '\\left|%0%\\right|',
'add': '\\left(%0%+%1%\\right)',
'ceil': '\\left\\lceil%0%\\right\\rceil',
'cube': '\\left(%0%\\right)^{3}',
'divide': '\\frac%0%%1%',
'dotDivide': '\\left(%0%' + exports.operators['dotDivide'] + '%1%\\right)',
'dotMultiply': '\\left(%0%' + exports.operators['dotMultiply'] + '%1%\\right)',
'dotPow': '\\left(%0%' + exports.operators['dotPow'] + '%1%\\right)',
'exp': '\\exp\\left(%0%\\right)',
'fix': defaultTemplate,
'floor': function (node, callbacks) {
return '\\left\\lfloor{' + node.args[0].toTex(callbacks) + '}\\right\\rfloor';
},
'gcd': function (node, callbacks) {
return '\\gcd\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'floor': '\\left\\lfloor%0%\\right\\rfloor',
'gcd': '\\gcd\\left(%*%\\right)',
'lcm': defaultTemplate,
'log10': function (node, callbacks) {
return '\\log_{10}\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'log': function (node, callbacks) {
'log10': '\\log_{10}\\left(%0%\\right)',
'log': function (node, callbacks) { //TODO use templates for that?
if (node.args.length === 1) {
return '\\ln\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
}
return '\\log_{' + node.args[1].toTex(callbacks)
+ '}\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'mod': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['mod'] + '{'
+ node.args[1] + '}\\right)';
},
'multiply': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['multiply'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'norm': function (node, callbacks) {
'mod': '\\left(%0%' + exports.operators['mod'] + '%1%\\right)',
'multiply': '\\left(%0%' + exports.operators['multiply'] + '%1%\\right)',
'norm': function (node, callbacks) { //TODO use templates for that?
if (node.args.length === 1) {
return '\\left\\|{' + node.args[0].toTex(callbacks) + '}\\right\\|';
}
@ -151,93 +145,39 @@ var functions = {
return '\\mathrm{norm}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
}
},
'nthRoot': function (node, callbacks) {
return '\\sqrt[' + node.args[1].toTex(callbacks)
+ ']{' + node.args[0].toTex(callbacks) + '}';
},
'pow': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}^{' + node.args[1].toTex(callbacks) + '}\\right)';
},
'round': function (node, callbacks) {
'nthRoot': '\\sqrt[%1%]%0%',
'pow': '\\left(%0%' + exports.operators['pow'] + '%1%\\right)',
'round': function (node, callbacks) { //TODO use templates for that?
if (node.args.length === 1) {
return '\\left\\lfloor{' + node.args[0].toTex(callbacks) + '}\\right\\rceil';
}
return '\\mathrm{round}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'sign': defaultTemplate,
'sqrt': function (node, callbacks) {
return '\\sqrt{' + node.args[0].toTex(callbacks) + '}';
},
'square': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks) + '}\\right)^{2}';
},
'subtract': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['subtract'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'unaryMinus': function (node, callbacks) {
return exports.operators['unaryMinus']
+ '\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'unaryPlus': function (node, callbacks) {
return exports.operators['unaryPlus']
+ '\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'sqrt': '\\sqrt%0%',
'square': '\\left(%0%\\right)^{2}',
'subtract': '\\left(%0%' + exports.operators['subtract'] + '%1%\\right)',
'unaryMinus': exports.operators['unaryMinus'] + '\\left(%0%\\right)',
'unaryPlus': exports.operators['unaryPlus'] + '\\left(%0%\\right)',
'xgcd': defaultTemplate,
//bitwise
'bitAnd': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['bitAnd'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'bitOr': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['bitOr'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'bitXor': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['bitXor'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'bitNot': function (node, callbacks) {
return '~\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'leftShift': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['leftShift'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'rightArithShift': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['rightArithShift'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'rightLogShift': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['rightLogShift'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'bitAnd': '\\left(%0%' + exports.operators['bitAnd'] + '%1%\\right)',
'bitOr': '\\left(%0%' + exports.operators['bitOr'] + '%1%\\right)',
'bitXor': '\\left(%0%' + exports.operators['bitXor'] + '%1%\\right)',
'bitNot': exports.operators['bitNot'] + '\\left(%0%\\right)',
'leftShift': '\\left(%0%' + exports.operators['leftShift'] + '%1%\\right)',
'rightArithShift': '\\left(%0%' + exports.operators['rightArithShift'] + '%1%\\right)',
'rightLogShift': '\\left(%0%' + exports.operators['rightLogShift'] + '%1%\\right)',
//complex
'arg': function (node, callbacks) {
return '\\arg\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'conj': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks) + '}\\right)^{*}';
},
'im': function (node, callbacks) {
return '\\Im\\left\\lbrace{' + node.args[0].toTex(callbacks) + '}\\right\\rbrace';
},
're': function (node, callbacks) {
return '\\Re\\left\\lbrace{' + node.args[0].toTex(callbacks) + '}\\right\\rbrace';
},
'arg': '\\arg\\left(%0%\\right)',
'conj': '\\left(%0%\\right)^{*}',
'im': '\\Im\\left\\lbrace%0%\\right\\rbrace',
're': '\\Re\\left\\lbrace%0%\\right\\rbrace',
//construction
'bignumber': function (node, callbacks) {
'bignumber': function (node, callbacks) { //TODO use templates for that?
if (node.args.length === 0) {
return '0';
}
@ -245,7 +185,7 @@ var functions = {
},
'boolean': defaultTemplate,
'chain': defaultTemplate,
'complex': function (node, callbacks) {
'complex': function (node, callbacks) { //TODO use templates for that?
switch (node.args.length) {
case 0:
return '0';
@ -257,13 +197,13 @@ var functions = {
}
},
'index': defaultTemplate,
'matrix': function (node, callbacks) {
'matrix': function (node, callbacks) { //TODO use templates for that?
if (node.args.length === 0) {
return '\\begin{bmatrix}\\end{bmatrix}';
}
return '\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'number': function (node, callbacks) {
'number': function (node, callbacks) { //TODO use templates for that?
switch (node.args.length) {
case 0:
return '0';
@ -275,7 +215,7 @@ var functions = {
}
},
'parser': defaultTemplate,
'string': function (node, callbacks) {
'string': function (node, callbacks) { //TODO use templates for that?
if (node.args.length === 0) {
return '""';
}
@ -296,70 +236,35 @@ var functions = {
'parse': defaultTemplate,
//logical
'and': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['and'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'not': function (node, callbacks) {
return exports.operators['not'] + '\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'or': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['or'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'xor': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['xor'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'and': '\\left(%0%' + exports.operators['and'] + '%1%\\right)',
'not': exports.operators['not'] + '\\left(%0%\\right)',
'or': '\\left(%0%' + exports.operators['or'] + '%1%\\right)',
'xor': '\\left(%0%' + exports.operators['xor'] + '%1%\\right)',
//matrix
'concat': defaultTemplate,
'cross': function (node, callbacks) {
return '{' + node.args[0].toTex(callbacks)
+ '}\\times{' + node.args[1].toTex(callbacks) + '}';
},
'det': function (node, callbacks) {
return '\\det\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'cross': '%0%\\times%1%',
'det': '\\det\\left(%0%\\right)',
'diag': defaultTemplate,
'dot': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}\\cdot{' + node.args[1].toTex(callbacks) + '}\\right)';
},
'dot': '\\left(%0%\\cdot%1%\\right)',
'eye': defaultTemplate,
'flatten': defaultTemplate,
'inv': function (node, callbacks) {
return '{' + node.args[0].toTex(callbacks) + '}^{-1}';
},
'inv': '%0%^{-1}',
'ones': defaultTemplate,
'range': defaultTemplate,
'resize': defaultTemplate,
'size': defaultTemplate,
'squeeze': defaultTemplate,
'subset': defaultTemplate,
'trace': function (node, callbacks) {
return '\\mathrm{tr}\\left({' + node.args[0].toTex() + '}\\right)';
},
'transpose': function (node, callbacks) {
return '{' + node.args[0].toTex(callbacks) + '}^{\\top}';
},
'trace': '\\mathrm{tr}\\left(%0%\\right)',
'transpose': '%0%^{\\top}',
'zeros': defaultTemplate,
//probability
'combinations': function (node, callbacks) {
return '\\binom{' + node.args[0].toTex(callbacks)
+ '}{' + node.args[1].toTex(callbacks) + '}';
},
'combinations': '\\binom%0%%1%',
'distribution': defaultTemplate,
'factorial': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks) + '}\\right)!';
},
'gamma': function (node, callbacks) {
return '\\Gamma\\left({' + node.args[0].toTex(callbacks) + '}\\right)';
},
'factorial': '\\left(%0%\\right)!',
'gamma': '\\Gamma\\left(%0%\\right)',
'permutations': defaultTemplate,
'pickRandom': defaultTemplate,
'randomInt': defaultTemplate,
@ -368,136 +273,52 @@ var functions = {
//relational
'compare': defaultTemplate,
'deepEqual': defaultTemplate,
'equal': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['equal'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'largerEq': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['largerEq'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'larger': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['larger'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'smallerEq': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['smallerEq'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'smaller': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['smaller'] + '{'
+ node.args[1].toTex(callbacks) + '}\\right)';
},
'unequal': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['unequal'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'equal': '\\left(%0%' + exports.operators['equal'] + '%1%\\right)',
'largerEq': '\\left(%0%' + exports.operators['largerEq'] + '%1%\\right)',
'larger': '\\left(%0%' + exports.operators['larger'] + '%1%\\right)',
'smallerEq': '\\left(%0%' + exports.operators['smallerEq'] + '%1%\\right)',
'smaller': '\\left(%0%' + exports.operators['smaller'] + '%1%\\right)',
'unequal': '\\left(%0%' + exports.operators['unequal'] + '%1%\\right)',
//statistics
'max': function (node, callbacks) {
return '\\max\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'max': '\\max\\left(%*%\\right)',
'mean': defaultTemplate,
'median': defaultTemplate,
'min': function (node, callbacks) {
return '\\min\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'min': '\\min\\left(%*%\\right)',
'prod': defaultTemplate,
'std': defaultTemplate,
'sum': defaultTemplate,
'var': function (node, callbacks) {
return '\\mathrm{Var}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'var': '\\mathrm{Var}\\left(%*%\\right)',
//trigonometry
'acosh': function (node, callbacks) {
return '\\cosh^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'acos': function (node, callbacks) {
return '\\cos^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'acoth': function (node, callbacks) {
return '\\coth^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'acot': function (node, callbacks) {
return '\\cot^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'acsch': function (node, callbacks) {
return '\\mathrm{csch}^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'acsc': function (node, callbacks) {
return '\\csc^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'asech': function (node, callbacks) {
return '\\mathrm{sech}^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'asec': function (node, callbacks) {
return '\\sec^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'asinh': function (node, callbacks) {
return '\\sinh^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'asin': function (node, callbacks) {
return '\\sin^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'atan2': function (node, callbacks) {
return '\\mathrm{atan2}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'atanh': function (node, callbacks) {
return '\\tanh^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'atan': function (node, callbacks) {
return '\\tan^{-1}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'cosh': function (node, callbacks) {
return '\\cosh\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'cos': function (node, callbacks) {
return '\\cos\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'coth': function (node, callbacks) {
return '\\coth\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'cot': function (node, callbacks) {
return '\\cot\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'csch': function (node, callbacks) {
return '\\mathrm{csch}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'csc': function (node, callbacks) {
return '\\csc\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'sech': function (node, callbacks) {
return '\\mathrm{sech}\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'sec': function (node, callbacks) {
return '\\sec\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'sinh': function (node, callbacks) {
return '\\sinh\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'sin': function (node, callbacks) {
return '\\sin\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'tanh': function (node, callbacks) {
return '\\tanh\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'tan': function (node, callbacks) {
return '\\tan\\left(' + functionArgs(node.args, callbacks) + '\\right)';
},
'acosh': '\\cosh^{-1}\\left(%0%\\right)',
'acos': '\\cos^{-1}\\left(%0%\\right)',
'acoth': '\\coth^{-1}\\left(%0%\\right)',
'acot': '\\cot^{-1}\\left(%0%\\right)',
'acsch': '\\mathrm{csch}^{-1}\\left(%0%\\right)',
'acsc': '\\csc^{-1}\\left(%0%\\right)',
'asech': '\\mathrm{sech}^{-1}\\left(%0%\\right)',
'asec': '\\sec^{-1}\\left(%0%\\right)',
'asinh': '\\sinh^{-1}\\left(%0%\\right)',
'asin': '\\sin^{-1}\\left(%0%\\right)',
'atan2': '\\mathrm{atan2}\\left(%*%\\right)',
'atanh': '\\tanh^{-1}\\left(%0%\\right)',
'atan': '\\tan^{-1}\\left(%0%\\right)',
'cosh': '\\cosh\\left(%0%\\right)',
'cos': '\\cos\\left(%0%\\right)',
'coth': '\\coth\\left(%0%\\right)',
'cot': '\\cot\\left(%0%\\right)',
'csch': '\\mathrm{csch}\\left(%0%\\right)',
'csc': '\\csc\\left(%0%\\right)',
'sech': '\\mathrm{sech}\\left(%0%\\right)',
'sec': '\\sec\\left(%0%\\right)',
'sinh': '\\sinh\\left(%0%\\right)',
'sin': '\\sin\\left(%0%\\right)',
'tanh': '\\tanh\\left(%0%\\right)',
'tan': '\\tan\\left(%0%\\right)',
//units
'to': function (node, callbacks) {
return '\\left({' + node.args[0].toTex(callbacks)
+ '}' + exports.operators['to'] + '{' + node.args[1].toTex(callbacks)
+ '}\\right)';
},
'to': '\\left(%0%' + exports.operators['to'] + '%1%\\right)',
//utils
'clone': defaultTemplate,
@ -511,40 +332,6 @@ var functions = {
'typeof': defaultTemplate
};
exports.operators = {
'transpose': '^{\\top}',
'factorial': '!',
'pow': '^',
'dotPow': '.^{\\wedge}', //TODO find ideal solution
'unaryPlus': '+',
'unaryMinus': '-',
'bitNot': '~', //TODO find ideal solution
'not': '\\neg',
'multiply': '\\cdot',
'divide': '\\frac', //TODO how to handle that properly?
'dotMultiply': '.\\cdot', //TODO find ideal solution
'dotDivide': '.:', //TODO find ideal solution
'mod': '\\mod',
'add': '+',
'subtract': '-',
'to': '\\rightarrow',
'leftShift': '<<',
'rightArithShift': '>>',
'rightLogShift': '>>>',
'equal': '=',
'unequal': '\\neq',
'smaller': '<',
'larger': '>',
'smallerEq': '\\leq',
'largerEq': '\\geq',
'bitAnd': '\\&',
'bitXor': '\\underline{|}',
'bitOr': '|',
'and': '\\wedge',
'xor': '\\veebar',
'or': '\\vee'
};
var units = {
deg: '^{\\circ}'
};

View File

@ -111,7 +111,7 @@ describe('nthRoot', function() {
it('should LaTeX nthRoot', function () {
var expression = math.parse('nthRoot(8,3)');
assert.equal(expression.toTex(), '\\sqrt[3]{8}');
assert.equal(expression.toTex(), '\\sqrt[{3}]{8}');
});
});