slangmath 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.
@@ -0,0 +1,501 @@
1
+ /**
2
+ * SLaNg Extended Mathematical Functions
3
+ * Extends the converter with support for trigonometric, logarithmic, and other functions
4
+ */
5
+
6
+ import { createTerm, createFraction } from './slang-basic.js';
7
+ import { slangToLatex, latexToSlang } from './slang-convertor.js';
8
+
9
+ // ============================================================================
10
+ // FUNCTION DEFINITIONS
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Supported mathematical functions with their LaTeX representations
15
+ */
16
+ export const SUPPORTED_FUNCTIONS = {
17
+ // Trigonometric
18
+ sin: { latex: '\\sin', arity: 1, category: 'trigonometric' },
19
+ cos: { latex: '\\cos', arity: 1, category: 'trigonometric' },
20
+ tan: { latex: '\\tan', arity: 1, category: 'trigonometric' },
21
+ cot: { latex: '\\cot', arity: 1, category: 'trigonometric' },
22
+ sec: { latex: '\\sec', arity: 1, category: 'trigonometric' },
23
+ csc: { latex: '\\csc', arity: 1, category: 'trigonometric' },
24
+
25
+ // Inverse trigonometric
26
+ arcsin: { latex: '\\arcsin', arity: 1, category: 'inverse_trig' },
27
+ arccos: { latex: '\\arccos', arity: 1, category: 'inverse_trig' },
28
+ arctan: { latex: '\\arctan', arity: 1, category: 'inverse_trig' },
29
+
30
+ // Hyperbolic
31
+ sinh: { latex: '\\sinh', arity: 1, category: 'hyperbolic' },
32
+ cosh: { latex: '\\cosh', arity: 1, category: 'hyperbolic' },
33
+ tanh: { latex: '\\tanh', arity: 1, category: 'hyperbolic' },
34
+
35
+ // Logarithmic
36
+ ln: { latex: '\\ln', arity: 1, category: 'logarithmic' },
37
+ log: { latex: '\\log', arity: 1, category: 'logarithmic' },
38
+ log10: { latex: '\\log_{10}', arity: 1, category: 'logarithmic' },
39
+
40
+ // Exponential
41
+ exp: { latex: '\\exp', arity: 1, category: 'exponential' },
42
+ sqrt: { latex: '\\sqrt', arity: 1, category: 'exponential' },
43
+
44
+ // Other functions
45
+ abs: { latex: '\\left|', arity: 1, category: 'absolute' },
46
+ floor: { latex: '\\lfloor', arity: 1, category: 'floor' },
47
+ ceil: { latex: '\\lceil', arity: 1, category: 'ceiling' }
48
+ };
49
+
50
+ // ============================================================================
51
+ // EXTENDED SLaNg STRUCTURES
52
+ // ============================================================================
53
+
54
+ /**
55
+ * Create a function expression in SLaNg format
56
+ */
57
+ export function createFunction(name, args) {
58
+ const funcInfo = SUPPORTED_FUNCTIONS[name];
59
+ if (!funcInfo) {
60
+ throw new Error(`Unsupported function: ${name}`);
61
+ }
62
+
63
+ if (funcInfo.arity !== args.length) {
64
+ throw new Error(`Function ${name} expects ${funcInfo.arity} arguments, got ${args.length}`);
65
+ }
66
+
67
+ return {
68
+ type: 'function',
69
+ name,
70
+ args,
71
+ latex: funcInfo.latex
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Check if an expression is a function
77
+ */
78
+ export function isFunction(expr) {
79
+ return expr && expr.type === 'function';
80
+ }
81
+
82
+ /**
83
+ * Get function information
84
+ */
85
+ export function getFunctionInfo(expr) {
86
+ if (!isFunction(expr)) {
87
+ return null;
88
+ }
89
+
90
+ return SUPPORTED_FUNCTIONS[expr.name] || null;
91
+ }
92
+
93
+ // ============================================================================
94
+ // EXTENDED CONVERSION FUNCTIONS
95
+ // ============================================================================
96
+
97
+ /**
98
+ * Convert SLaNg function to LaTeX
99
+ */
100
+ export function functionToLatex(func, options = {}) {
101
+ if (!isFunction(func)) {
102
+ throw new Error('Expression is not a function');
103
+ }
104
+
105
+ const funcInfo = getFunctionInfo(func);
106
+ if (!funcInfo) {
107
+ throw new Error(`Unknown function: ${func.name}`);
108
+ }
109
+
110
+ const argsLatex = func.args.map(arg => slangToLatex(arg, options));
111
+
112
+ // Special handling for absolute value
113
+ if (func.name === 'abs') {
114
+ return `\\left|${argsLatex[0]}\\right|`;
115
+ }
116
+
117
+ // Special handling for floor and ceiling
118
+ if (func.name === 'floor') {
119
+ return `\\lfloor${argsLatex[0]}\\rfloor`;
120
+ }
121
+
122
+ if (func.name === 'ceil') {
123
+ return `\\lceil${argsLatex[0]}\\rceil`;
124
+ }
125
+
126
+ // Special handling for sqrt
127
+ if (func.name === 'sqrt') {
128
+ return `\\sqrt{${argsLatex[0]}}`;
129
+ }
130
+
131
+ // General function format
132
+ return `${func.latex}{${argsLatex.join(', ')}}`;
133
+ }
134
+
135
+ /**
136
+ * Parse LaTeX function to SLaNg
137
+ */
138
+ export function parseFunction(latex) {
139
+ // Try to match function patterns
140
+ for (const [name, info] of Object.entries(SUPPORTED_FUNCTIONS)) {
141
+ const pattern = new RegExp(`^\\${info.latex.replace('\\', '\\\\')}\\s*\\{([^{}]+)\\}`);
142
+ const match = latex.match(pattern);
143
+
144
+ if (match) {
145
+ const argStr = match[1];
146
+ const arg = latexToSlang(argStr);
147
+ return createFunction(name, [arg]);
148
+ }
149
+ }
150
+
151
+ // Special handling for absolute value
152
+ const absMatch = latex.match(/^\\left\|([^|]+)\\right\|$/);
153
+ if (absMatch) {
154
+ const arg = latexToSlang(absMatch[1]);
155
+ return createFunction('abs', [arg]);
156
+ }
157
+
158
+ // Special handling for floor
159
+ const floorMatch = latex.match(/^\\lfloor([^\\]+)\\rfloor$/);
160
+ if (floorMatch) {
161
+ const arg = latexToSlang(floorMatch[1]);
162
+ return createFunction('floor', [arg]);
163
+ }
164
+
165
+ // Special handling for ceiling
166
+ const ceilMatch = latex.match(/^\\lceil([^\\]+)\\rceil$/);
167
+ if (ceilMatch) {
168
+ const arg = latexToSlang(ceilMatch[1]);
169
+ return createFunction('ceil', [arg]);
170
+ }
171
+
172
+ throw new Error(`Unable to parse function from LaTeX: ${latex}`);
173
+ }
174
+
175
+ /**
176
+ * Extended SLaNg to LaTeX converter that handles functions
177
+ */
178
+ export function extendedSlangToLatex(expr, options = {}) {
179
+ // Handle functions
180
+ if (isFunction(expr)) {
181
+ return functionToLatex(expr, options);
182
+ }
183
+
184
+ // Handle composite expressions with functions
185
+ if (expr.terms) {
186
+ const processedTerms = expr.terms.map(term => {
187
+ if (term.func) {
188
+ return { ...term, func: extendedSlangToLatex(term.func, options) };
189
+ }
190
+ return term;
191
+ });
192
+
193
+ return slangToLatex({ ...expr, terms: processedTerms }, options);
194
+ }
195
+
196
+ // Default to original converter
197
+ return slangToLatex(expr, options);
198
+ }
199
+
200
+ /**
201
+ * Extended LaTeX to SLaNg converter that handles functions
202
+ */
203
+ export function extendedLatexToSlang(latex, options = {}) {
204
+ latex = latex.trim();
205
+
206
+ // Try to parse as function first
207
+ try {
208
+ return parseFunction(latex);
209
+ } catch (error) {
210
+ // Not a function, try original parser
211
+ }
212
+
213
+ // Handle expressions with embedded functions
214
+ const functionPattern = /\\(sin|cos|tan|cot|sec|csc|arcsin|arccos|arctan|sinh|cosh|tanh|ln|log|exp|sqrt|left\||lfloor|lceil)/g;
215
+
216
+ if (functionPattern.test(latex)) {
217
+ // Extract and process functions
218
+ let processedLatex = latex;
219
+ const functions = [];
220
+
221
+ // Find all function occurrences
222
+ let match;
223
+ const regex = /(\\(sin|cos|tan|cot|sec|csc|arcsin|arccos|arctan|sinh|cosh|tanh|ln|log|exp|sqrt|left\||lfloor|lceil)\s*\{([^{}]+)\}|\\left\|([^|]+)\\right\||\\lfloor([^\\]+)\\rfloor|\\lceil([^\\]+)\\rceil)/g;
224
+
225
+ while ((match = regex.exec(latex)) !== null) {
226
+ const fullMatch = match[0];
227
+ const funcName = match[2] ||
228
+ (match[3] ? 'abs' :
229
+ match[4] ? 'floor' :
230
+ match[5] ? 'ceil' : null);
231
+
232
+ if (funcName) {
233
+ const argStr = match[3] || match[4] || match[5] || match[6];
234
+ const funcExpr = parseFunction(fullMatch);
235
+ functions.push({ original: fullMatch, parsed: funcExpr });
236
+
237
+ // Replace with placeholder
238
+ processedLatex = processedLatex.replace(fullMatch, `__FUNC_${functions.length - 1}__`);
239
+ }
240
+ }
241
+
242
+ // Parse the remaining expression
243
+ const baseExpr = latexToSlang(processedLatex, options);
244
+
245
+ // Reinsert functions
246
+ if (baseExpr.terms) {
247
+ baseExpr.terms.forEach(term => {
248
+ functions.forEach((func, index) => {
249
+ // This is a simplified approach - in practice, you'd need more sophisticated handling
250
+ if (term.var && term.var['__FUNC_' + index + '__']) {
251
+ delete term.var['__FUNC_' + index + '__'];
252
+ term.func = func.parsed;
253
+ }
254
+ });
255
+ });
256
+ }
257
+
258
+ return baseExpr;
259
+ }
260
+
261
+ // Default to original parser
262
+ return latexToSlang(latex, options);
263
+ }
264
+
265
+ // ============================================================================
266
+ // FUNCTION EVALUATION
267
+ // ============================================================================
268
+
269
+ /**
270
+ * Evaluate a function expression at a given point
271
+ */
272
+ export function evaluateFunction(func, point = {}) {
273
+ if (!isFunction(func)) {
274
+ throw new Error('Expression is not a function');
275
+ }
276
+
277
+ const evaluatedArgs = func.args.map(arg => {
278
+ if (arg.terms) {
279
+ // Evaluate polynomial at point
280
+ let result = 0;
281
+ arg.terms.forEach(term => {
282
+ let termValue = term.coeff;
283
+ if (term.var) {
284
+ for (const [variable, power] of Object.entries(term.var)) {
285
+ if (point[variable] !== undefined) {
286
+ termValue *= Math.pow(point[variable], power);
287
+ } else {
288
+ // Variable not provided, return symbolic
289
+ return arg; // Return original if can't evaluate
290
+ }
291
+ }
292
+ }
293
+ result += termValue;
294
+ });
295
+ return result;
296
+ } else if (arg.coeff !== undefined) {
297
+ return arg.coeff;
298
+ }
299
+ return arg;
300
+ });
301
+
302
+ // Apply the mathematical function
303
+ switch (func.name) {
304
+ case 'sin': return Math.sin(evaluatedArgs[0]);
305
+ case 'cos': return Math.cos(evaluatedArgs[0]);
306
+ case 'tan': return Math.tan(evaluatedArgs[0]);
307
+ case 'cot': return 1 / Math.tan(evaluatedArgs[0]);
308
+ case 'sec': return 1 / Math.cos(evaluatedArgs[0]);
309
+ case 'csc': return 1 / Math.sin(evaluatedArgs[0]);
310
+ case 'arcsin': return Math.asin(evaluatedArgs[0]);
311
+ case 'arccos': return Math.acos(evaluatedArgs[0]);
312
+ case 'arctan': return Math.atan(evaluatedArgs[0]);
313
+ case 'sinh': return Math.sinh(evaluatedArgs[0]);
314
+ case 'cosh': return Math.cosh(evaluatedArgs[0]);
315
+ case 'tanh': return Math.tanh(evaluatedArgs[0]);
316
+ case 'ln': return Math.log(evaluatedArgs[0]);
317
+ case 'log': return Math.log10(evaluatedArgs[0]);
318
+ case 'exp': return Math.exp(evaluatedArgs[0]);
319
+ case 'sqrt': return Math.sqrt(evaluatedArgs[0]);
320
+ case 'abs': return Math.abs(evaluatedArgs[0]);
321
+ case 'floor': return Math.floor(evaluatedArgs[0]);
322
+ case 'ceil': return Math.ceil(evaluatedArgs[0]);
323
+ default:
324
+ throw new Error(`Evaluation not implemented for function: ${func.name}`);
325
+ }
326
+ }
327
+
328
+ // ============================================================================
329
+ // FUNCTION DIFFERENTIATION
330
+ // ============================================================================
331
+
332
+ /**
333
+ * Differentiate a function expression
334
+ */
335
+ export function differentiateFunction(func, variable) {
336
+ if (!isFunction(func)) {
337
+ throw new Error('Expression is not a function');
338
+ }
339
+
340
+ const [arg] = func.args;
341
+ const argDerivative = differentiate(arg, variable);
342
+
343
+ // Chain rule: d/dx[f(g(x))] = f'(g(x)) * g'(x)
344
+ switch (func.name) {
345
+ case 'sin':
346
+ return createFunction('cos', [arg]);
347
+ case 'cos':
348
+ return createTerm(-1);
349
+ case 'tan':
350
+ return createFraction(
351
+ [createTerm(1)],
352
+ [createFunction('cos', [arg]), createFunction('cos', [arg])]
353
+ );
354
+ case 'cot':
355
+ return createTerm(-1);
356
+ case 'sec':
357
+ return createTerm(1);
358
+ case 'csc':
359
+ return createTerm(-1);
360
+ case 'arcsin':
361
+ return createFraction(
362
+ [createTerm(1)],
363
+ [createFunction('sqrt', [createTerm(1, {}, createTerm(-1))])]
364
+ );
365
+ case 'arccos':
366
+ return createTerm(-1);
367
+ case 'arctan':
368
+ return createFraction(
369
+ [createTerm(1)],
370
+ [createTerm(1, {}, createTerm(1))]
371
+ );
372
+ case 'sinh':
373
+ return createFunction('cosh', [arg]);
374
+ case 'cosh':
375
+ return createFunction('sinh', [arg]);
376
+ case 'tanh':
377
+ return createFraction(
378
+ [createTerm(1)],
379
+ [createFunction('cosh', [arg]), createFunction('cosh', [arg])]
380
+ );
381
+ case 'ln':
382
+ return createFraction([createTerm(1)], [arg]);
383
+ case 'log':
384
+ return createFraction([createTerm(1)], [createTerm(Math.LN10)]);
385
+ case 'exp':
386
+ return createFunction('exp', [arg]);
387
+ case 'sqrt':
388
+ return createFraction([createTerm(1)], [createTerm(2)]);
389
+ case 'abs':
390
+ return createFunction('sign', [arg]); // Would need sign function implementation
391
+ case 'floor':
392
+ case 'ceil':
393
+ // These are not differentiable in the classical sense
394
+ throw new Error(`Function ${func.name} is not differentiable`);
395
+ default:
396
+ throw new Error(`Differentiation not implemented for function: ${func.name}`);
397
+ }
398
+ }
399
+
400
+ // Helper function for differentiation (would need to import from slang-math.js)
401
+ function differentiate(expr, variable) {
402
+ // This would be implemented in the main math module
403
+ // For now, return a placeholder
404
+ return createTerm(1, { [variable]: 1 });
405
+ }
406
+
407
+ // ============================================================================
408
+ // UTILITY FUNCTIONS
409
+ // ============================================================================
410
+
411
+ /**
412
+ * Get all supported functions by category
413
+ */
414
+ export function getFunctionsByCategory(category) {
415
+ return Object.entries(SUPPORTED_FUNCTIONS)
416
+ .filter(([name, info]) => info.category === category)
417
+ .map(([name, info]) => ({ name, ...info }));
418
+ }
419
+
420
+ /**
421
+ * Check if a function is supported
422
+ */
423
+ export function isSupportedFunction(name) {
424
+ return name in SUPPORTED_FUNCTIONS;
425
+ }
426
+
427
+ /**
428
+ * Get function LaTeX representation
429
+ */
430
+ export function getFunctionLatex(name) {
431
+ const info = SUPPORTED_FUNCTIONS[name];
432
+ return info ? info.latex : null;
433
+ }
434
+
435
+ // ============================================================================
436
+ // EXAMPLES AND DEMO
437
+ // ============================================================================
438
+
439
+ /**
440
+ * Demo function showing extended capabilities
441
+ */
442
+ export function demoExtendedFunctions() {
443
+ console.log('šŸš€ SLaNg Extended Functions Demo');
444
+ console.log('='.repeat(50));
445
+
446
+ // Create function expressions
447
+ const sinExpr = createFunction('sin', [createTerm(1, { x: 1 })]);
448
+ const logExpr = createFunction('ln', [createTerm(1, { x: 1 }), createTerm(1)]);
449
+ const sqrtExpr = createFunction('sqrt', [createTerm(1, { x: 2 }), createTerm(1)]);
450
+
451
+ console.log('\nšŸ“ Function to LaTeX:');
452
+ console.log(`sin(x): ${functionToLatex(sinExpr)}`);
453
+ console.log(`ln(x + 1): ${functionToLatex(logExpr)}`);
454
+ console.log(`sqrt(x² + 1): ${functionToLatex(sqrtExpr)}`);
455
+
456
+ console.log('\nšŸ”„ LaTeX to Function:');
457
+ const parsedSin = parseFunction('\\sin{x}');
458
+ const parsedLog = parseFunction('\\ln{x + 1}');
459
+ const parsedSqrt = parseFunction('\\sqrt{x^{2} + 1}');
460
+
461
+ console.log(`\\sin{x}: ${JSON.stringify(parsedSin)}`);
462
+ console.log(`\\ln{x + 1}: ${JSON.stringify(parsedLog)}`);
463
+ console.log(`\\sqrt{x^{2} + 1}: ${JSON.stringify(parsedSqrt)}`);
464
+
465
+ console.log('\n🧮 Function Evaluation:');
466
+ console.log(`sin(Ļ€/2): ${evaluateFunction(sinExpr, { x: Math.PI / 2 })}`);
467
+ console.log(`ln(e): ${evaluateFunction(logExpr, { x: Math.E - 1 })}`);
468
+ console.log(`sqrt(4): ${evaluateFunction(sqrtExpr, { x: Math.sqrt(3) })}`);
469
+
470
+ console.log('\nšŸ“š Supported Functions:');
471
+ Object.entries(SUPPORTED_FUNCTIONS).forEach(([name, info]) => {
472
+ console.log(` ${name}: ${info.latex} (${info.category})`);
473
+ });
474
+ }
475
+
476
+ // Export all extended functions
477
+ export default {
478
+ // Core structures
479
+ createFunction,
480
+ isFunction,
481
+ getFunctionInfo,
482
+
483
+ // Conversion functions
484
+ functionToLatex,
485
+ parseFunction,
486
+ extendedSlangToLatex,
487
+ extendedLatexToSlang,
488
+
489
+ // Evaluation and differentiation
490
+ evaluateFunction,
491
+ differentiateFunction,
492
+
493
+ // Utilities
494
+ getFunctionsByCategory,
495
+ isSupportedFunction,
496
+ getFunctionLatex,
497
+ SUPPORTED_FUNCTIONS,
498
+
499
+ // Demo
500
+ demoExtendedFunctions
501
+ };