mathjs/lib/util/latex.js

501 lines
9.1 KiB
JavaScript

'use strict';
// FIXME: remove dependencies on Nodes
var ArrayNode = require('../expression/node/ArrayNode');
var OperatorNode = require('../expression/node/OperatorNode');
var SymbolNode = require('../expression/node/SymbolNode');
var ConstantNode = require('../expression/node/ConstantNode');
// GREEK LETTERS
var greek = {
Alpha: 'A', alpha: true,
Beta: 'B', beta: true,
Gamma: true, gamma: true,
Delta: true, delta: true,
Epsilon: 'E', epsilon: true, varepsilon: true,
Zeta: 'Z', zeta: true,
Eta: 'H', eta: true,
Theta: true, theta: true, vartheta: true,
Iota: 'I', iota: true,
Kappa: 'K', kappa: true, varkappa: true,
Lambda: true, lambda: true,
Mu: 'M', mu: true,
Nu: 'N', nu: true,
Xi: true, xi: true,
Omicron: 'O', omicron: true,
Pi: true, pi: true, varpi: true,
Rho: 'P', rho: true, varrho: true,
Sigma: true, sigma: true, varsigma: true,
Tau: 'T', tau: true,
Upsilon: true, upsilon: true,
Phi: true, phi: true, varphi: true,
Chi: 'X', chi: true,
Psi: true, psi: true,
Omega: true, omega: true
};
var dots = {
dots: true,
ldots: true,
cdots: true,
vdots: true,
ddots: true,
idots: true
};
var logic = {
'true': '\\mathrm{True}',
'false': '\\mathrm{False}'
};
var other = {
inf: '\\infty',
Inf: '\\infty',
infinity: '\\infty',
Infinity: '\\infty',
oo: '\\infty',
lim: true,
'undefined': '\\mathbf{?}'
};
// FUNCTIONS
var functions = {
acos: '\\cos^{-1}',
arccos: '\\cos^{-1}',
cos: true,
csc: true,
csch: false,
exp: true,
ker: true,
limsup: true,
min: true,
sinh: true,
asin: '\\sin^{-1}',
arcsin: '\\sin^{-1}',
cosh: true,
deg: true,
gcd: true,
lg: true,
ln: true,
Pr: true,
sup: true,
atan: '\\tan^{-1}',
atan2: '\\tan2^{-1}',
arctan: '\\tan^{-1}',
cot: true,
det: true,
hom: true,
log: true,
log10: '\\log_{10}',
sec: true,
sech: false,
tan: true,
arg: true,
coth: true,
dim: true,
inf: true,
max: true,
sin: true,
tanh: true,
fix: false,
lcm: false,
sign: false,
xgcd: false,
unaryMinus: false,
unaryPlus: false,
// complex
complex: false,
conj: false,
im: false,
re: false,
// matrix
diag: false,
resize: false,
size: false,
squeeze: false,
subset: false,
index: false,
ones: false,
zeros: false,
range: false,
// probability
random: false,
// statistics
mean: '\\mu',
median: false,
prod: false,
std: '\\sigma',
'var': '\\sigma^2'
};
// CURLY FUNCTIONS
// wrap parameters with {}
var curlyFunctions = {
sqrt: true,
inv: true,
int: '\\int',
Int: '\\int',
integrate: '\\int',
eigenvalues: '\\lambda',
liminf: true,
lim: true,
exp: 'e^',
sum: true,
eye: '\\mathbf{I}'
};
var operators = {
'<=': '\\leq',
'>=': '\\geq',
'!=': '\\neq',
'in': true,
'*': '\\cdot',
'/': '\\frac',
'mod': '\\bmod',
'to': '\\rightarrow'
};
var units = {
deg: '^{\\circ}'
};
var symbols = {};
function mapSymbols() {
var args = Array.prototype.slice.call(arguments),
obj;
for (var i = 0, len = args.length; i < len; i++) {
obj = args[i];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
symbols[key] = obj[key];
}
}
}
}
mapSymbols(
functions,
curlyFunctions,
greek,
dots,
logic,
other
);
function latexIs(arr, value) {
return typeof arr[value] !== 'undefined';
}
function latexIsFn(arr) {
return function(value) {
return latexIs(arr, value);
};
}
function latexToFn(arr) {
return function(value) {
if (typeof arr[value] === 'boolean') {
if (arr[value] === true) {
value = '\\' + value;
}
else {
value = '\\mathrm{' + value + '}';
}
}
else if (typeof arr[value] === 'string') {
value = arr[value];
}
else if (typeof value === 'string') {
var index = value.indexOf('_');
if (index !== -1) {
value = exports.toSymbol(value.substring(0, index)) + '_{' +
exports.toSymbol(value.substring(index+1)) + '}';
}
}
return value;
};
}
exports.isSymbol = latexIsFn(symbols);
exports.toSymbol = latexToFn(symbols);
exports.isFunction = latexIsFn(functions);
exports.toFunction = latexToFn(functions);
exports.isCurlyFunction = latexIsFn(curlyFunctions);
exports.toCurlyFunction = latexToFn(curlyFunctions);
exports.isOperator = latexIsFn(operators);
exports.toOperator = latexToFn(operators);
exports.isUnit = latexIsFn(units);
exports.toUnit = (function() {
var _toUnit = latexToFn(units);
return function(value, notSpaced) {
if (exports.isUnit(value)) {
return _toUnit(value);
}
return (notSpaced ? '' : '\\,') + '\\mathrm{' + value + '}';
};
}());
exports.addBraces = function(s, brace, type) {
if (brace === null) {
return s;
}
var braces = ['', ''];
type = type || 'normal';
if (typeof brace === 'undefined' || brace === false) {
braces = ['{', '}'];
}
else if (brace === true) {
braces = ['(', ')'];
type = 'lr';
}
else if (Array.isArray(brace) && brace.length === 2) {
braces = brace;
}
else {
braces = [brace, brace];
}
switch (type) {
case 'normal':
case false:
return braces[0] + s + braces[1];
case 'lr':
return '\\left' + braces[0] + '{' + s + '}' + '\\right' + braces[1];
case 'be':
return '\\begin{' + braces[0] + '}' + s + '\\end{' + braces[1] + '}';
}
return braces[0] + s + braces[1];
};
exports.toArgs = function(that) {
var name = that.name,
args = that.args,
func = exports.toSymbol(that.name),
texParams = null,
brace = null,
type = false,
showFunc = false,
prefix = '',
suffix = '',
op = null;
switch (name) {
// OPERATORS
case 'add':
op = '+';
break;
case 'subtract':
op = '-';
break;
case 'larger':
op = '>';
break;
case 'largerEq':
op = '>=';
break;
case 'smaller':
op = '<';
break;
case 'smallerEq':
op = '<=';
break;
case 'unequal':
op = '!=';
break;
case 'equal':
op = '=';
break;
case 'mod':
op = 'mod';
break;
case 'multiply':
op = '*';
break;
case 'pow':
op = '^';
break;
case 'concat':
op = '||';
break;
case 'factorial':
op = '!';
break;
case 'permutations':
if (args.length === 1) {
if (args[0] instanceof SymbolNode || args[0] instanceof ConstantNode) {
op = '!';
}
else {
return '{\\left(' + args[0].toTex() + '\\right)!}';
}
}
else {
// op = 'P';
var n = args[0].toTex(),
k = args[1].toTex();
return '\\frac{' + n + '!}{\\left(' + n + ' - ' + k + '\\right)!}';
}
break;
// probability
case 'combinations':
op = '\\choose';
break;
// LR BRACES
case 'abs':
brace = '|';
type = 'lr';
break;
case 'norm':
brace = '\\|';
type = 'lr';
if (args.length === 2) {
var tmp = args[1].toTex();
if (tmp === '\\text{inf}') {
tmp = '\\infty';
}
else if (tmp === '\\text{-inf}') {
tmp = '{- \\infty}';
}
else if (tmp === '\\text{fro}') {
tmp = 'F';
}
suffix = '_{' + tmp + '}';
args = [args[0]];
}
break;
case 'ceil':
brace = ['\\lceil', '\\rceil'];
type = 'lr';
break;
case 'floor':
brace = ['\\lfloor', '\\rfloor'];
type = 'lr';
break;
case 'round':
brace = ['\\lfloor', '\\rceil'];
type = 'lr';
if (args.length === 2) {
suffix = '_' + exports.addBraces(args[1].toTex());
args = [args[0]];
}
break;
// NORMAL BRACES
case 'inv':
suffix = '^{-1}';
break;
case 'transpose':
suffix = '^{T}';
brace = false;
break;
// SPECIAL NOTATION
case 'log':
var base = 'e';
if (args.length === 2) {
base = args[1].toTex();
func = '\\log_{' + base + '}';
args = [args[0]];
}
if (base === 'e') {
func = '\\ln';
}
showFunc = true;
break;
case 'square':
suffix = '^{2}';
break;
case 'cube':
suffix = '^{3}';
break;
// MATRICES
case 'eye':
showFunc = true;
brace = false;
func += '_';
break;
case 'det':
if (that.args[0] instanceof ArrayNode) {
return that.args[0].toTex('vmatrix');
}
brace = 'vmatrix';
type = 'be';
break;
default:
showFunc = true;
break;
}
if (op !== null) {
brace = (op === '+' || op === '-');
texParams = (new OperatorNode(op, name, args)).toTex();
}
else {
op = ', ';
}
if (brace === null && !exports.isCurlyFunction(name)) {
brace = true;
}
texParams = texParams || args.map(function(param) {
return '{' + param.toTex() + '}' ;
}).join(op);
return prefix + (showFunc ? func : '') +
exports.addBraces(texParams, brace, type) +
suffix;
};