UNPKG

lsh-framework

Version:

A powerful, extensible shell with advanced job management, database persistence, and modern CLI features

257 lines (256 loc) 9.57 kB
/** * Floating Point Arithmetic Implementation * Provides ZSH-compatible floating point math support */ export class FloatingPointArithmetic { mathFunctions = new Map(); precision = 6; constructor() { this.setupMathFunctions(); } /** * Evaluate arithmetic expression with floating point support */ evaluate(expression) { try { // Clean and validate expression const cleaned = this.cleanExpression(expression); // Replace variables with their values const withVariables = this.replaceVariables(cleaned); // Replace function calls const withFunctions = this.replaceFunctions(withVariables); // Evaluate the expression const result = this.evaluateExpression(withFunctions); // Round to specified precision return this.roundToPrecision(result); } catch (error) { throw new Error(`Arithmetic error: ${error.message}`); } } /** * Set precision for floating point results */ setPrecision(precision) { this.precision = Math.max(0, Math.min(15, precision)); } /** * Get current precision */ getPrecision() { return this.precision; } /** * Add a custom math function */ addMathFunction(func) { this.mathFunctions.set(func.name, func); } /** * Get available math functions */ getAvailableFunctions() { return Array.from(this.mathFunctions.keys()); } /** * Setup built-in math functions */ setupMathFunctions() { // Basic trigonometric functions this.addMathFunction({ name: 'sin', func: Math.sin, arity: 1 }); this.addMathFunction({ name: 'cos', func: Math.cos, arity: 1 }); this.addMathFunction({ name: 'tan', func: Math.tan, arity: 1 }); this.addMathFunction({ name: 'asin', func: Math.asin, arity: 1 }); this.addMathFunction({ name: 'acos', func: Math.acos, arity: 1 }); this.addMathFunction({ name: 'atan', func: Math.atan, arity: 1 }); this.addMathFunction({ name: 'atan2', func: (x, y) => Math.atan2(y ?? 0, x), arity: 2 }); // Hyperbolic functions this.addMathFunction({ name: 'sinh', func: Math.sinh, arity: 1 }); this.addMathFunction({ name: 'cosh', func: Math.cosh, arity: 1 }); this.addMathFunction({ name: 'tanh', func: Math.tanh, arity: 1 }); this.addMathFunction({ name: 'asinh', func: Math.asinh, arity: 1 }); this.addMathFunction({ name: 'acosh', func: Math.acosh, arity: 1 }); this.addMathFunction({ name: 'atanh', func: Math.atanh, arity: 1 }); // Exponential and logarithmic functions this.addMathFunction({ name: 'exp', func: Math.exp, arity: 1 }); this.addMathFunction({ name: 'log', func: Math.log, arity: 1 }); this.addMathFunction({ name: 'log10', func: Math.log10, arity: 1 }); this.addMathFunction({ name: 'log2', func: Math.log2, arity: 1 }); this.addMathFunction({ name: 'pow', func: (x, y) => Math.pow(x, y ?? 0), arity: 2 }); // Power and root functions this.addMathFunction({ name: 'sqrt', func: Math.sqrt, arity: 1 }); this.addMathFunction({ name: 'cbrt', func: Math.cbrt, arity: 1 }); // Rounding functions this.addMathFunction({ name: 'ceil', func: Math.ceil, arity: 1 }); this.addMathFunction({ name: 'floor', func: Math.floor, arity: 1 }); this.addMathFunction({ name: 'round', func: Math.round, arity: 1 }); this.addMathFunction({ name: 'trunc', func: Math.trunc, arity: 1 }); // Absolute value and sign this.addMathFunction({ name: 'abs', func: Math.abs, arity: 1 }); this.addMathFunction({ name: 'sign', func: Math.sign, arity: 1 }); // Random functions this.addMathFunction({ name: 'random', func: () => Math.random(), arity: 1 }); this.addMathFunction({ name: 'rand', func: () => Math.random(), arity: 1 }); // Constants this.addMathFunction({ name: 'pi', func: () => Math.PI, arity: 1 }); this.addMathFunction({ name: 'e', func: () => Math.E, arity: 1 }); // Additional utility functions this.addMathFunction({ name: 'min', func: (x, y) => Math.min(x, y ?? x), arity: 2 }); this.addMathFunction({ name: 'max', func: (x, y) => Math.max(x, y ?? x), arity: 2 }); this.addMathFunction({ name: 'clz32', func: Math.clz32, arity: 1 }); this.addMathFunction({ name: 'fround', func: Math.fround, arity: 1 }); this.addMathFunction({ name: 'imul', func: (x, y) => Math.imul(x, y ?? 0), arity: 2 }); } /** * Clean expression by removing whitespace and validating characters */ cleanExpression(expression) { // Remove whitespace const cleaned = expression.replace(/\s/g, ''); // Validate characters (allow numbers, operators, parentheses, function names, and dots) if (!/^[0-9+\-*/().,a-zA-Z_]+$/.test(cleaned)) { throw new Error('Invalid characters in expression'); } return cleaned; } /** * Replace variables in expression (placeholder for future variable support) */ replaceVariables(expression) { // For now, just return the expression as-is // In a full implementation, this would replace variables like $x with their values return expression; } /** * Replace function calls with their results */ replaceFunctions(expression) { let result = expression; // Find function calls like sin(1.5) or pow(2,3) const functionRegex = /([a-zA-Z_][a-zA-Z0-9_]*)\(([^)]*)\)/g; let match; while ((match = functionRegex.exec(expression)) !== null) { const [fullMatch, funcName, argsStr] = match; const func = this.mathFunctions.get(funcName); if (!func) { throw new Error(`Unknown function: ${funcName}`); } const args = this.parseFunctionArguments(argsStr); if (args.length !== func.arity) { throw new Error(`Function ${funcName} expects ${func.arity} arguments, got ${args.length}`); } const funcResult = func.func(args[0], args[1]); result = result.replace(fullMatch, funcResult.toString()); } return result; } /** * Parse function arguments */ parseFunctionArguments(argsStr) { if (!argsStr.trim()) return []; const args = []; let current = ''; let parenCount = 0; for (let i = 0; i < argsStr.length; i++) { const char = argsStr[i]; if (char === '(') { parenCount++; current += char; } else if (char === ')') { parenCount--; current += char; } else if (char === ',' && parenCount === 0) { args.push(parseFloat(current.trim())); current = ''; } else { current += char; } } if (current.trim()) { args.push(parseFloat(current.trim())); } return args; } /** * Evaluate the mathematical expression */ evaluateExpression(expression) { try { // Use Function constructor for safe evaluation // This is a simplified approach - a full implementation would use a proper parser const func = new Function(`return ${expression}`); const result = func(); if (typeof result !== 'number' || isNaN(result)) { throw new Error('Invalid expression result'); } return result; } catch (error) { throw new Error(`Expression evaluation failed: ${error.message}`); } } /** * Round number to specified precision */ roundToPrecision(num) { const factor = Math.pow(10, this.precision); return Math.round(num * factor) / factor; } /** * Format number with specified precision */ formatNumber(num, precision) { const prec = precision !== undefined ? precision : this.precision; return num.toFixed(prec); } /** * Check if a string represents a valid floating point number */ isValidFloat(str) { const num = parseFloat(str); return !isNaN(num) && isFinite(num); } /** * Convert string to floating point number */ parseFloat(str) { const num = parseFloat(str); if (isNaN(num)) { throw new Error(`Invalid floating point number: ${str}`); } return num; } /** * Get mathematical constants */ getConstants() { return { pi: Math.PI, e: Math.E, ln2: Math.LN2, ln10: Math.LN10, log2e: Math.LOG2E, log10e: Math.LOG10E, sqrt1_2: Math.SQRT1_2, sqrt2: Math.SQRT2, }; } /** * Evaluate expression with error handling */ safeEvaluate(expression) { try { const result = this.evaluate(expression); return { success: true, result }; } catch (error) { return { success: false, error: error.message }; } } } export default FloatingPointArithmetic;