-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[TS] [ DHTML and TypeScript ] Add MathCalculator class for processing…
… mathematical formulas with support for nested expressions and square root operation
- Loading branch information
Showing
3 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// MathCalculator.ts | ||
// tsc MathCalculator.ts -t esnext | ||
export class MathCalculator { | ||
static processFormulaVerbose(line) { | ||
try { | ||
const result = MathCalculator.processFormulaString(line); | ||
console.log(`The result of '${line}' is '${result}'`); | ||
} | ||
catch (error) { | ||
console.error(`Error: ${error.message}`); | ||
} | ||
} | ||
static processFormulaString(line) { | ||
const words = line.split(" "); | ||
return MathCalculator.processFormulaWords(words, 0, words.length - 1); | ||
} | ||
static processFormulaWords(words, startIndex, endIndex) { | ||
if (endIndex < startIndex) | ||
throw new RangeError("Invalid index: endIndex < startIndex"); | ||
let currentOperator = null; | ||
let result = null; | ||
for (let currentIndex = startIndex; currentIndex <= endIndex; currentIndex++) { | ||
const word = words[currentIndex]; | ||
const isNumber = !isNaN(parseFloat(word)); | ||
if (isNumber || word === "(") { | ||
let number; | ||
if (word === "(") { | ||
let depth = 0; | ||
let closingIndex = -1; | ||
for (let searchClosingIndex = currentIndex; searchClosingIndex <= endIndex; searchClosingIndex++) { | ||
if (words[searchClosingIndex] === "(") | ||
depth++; | ||
else if (words[searchClosingIndex] === ")") | ||
depth--; | ||
if (words[searchClosingIndex] === ")" && depth === 0) { | ||
closingIndex = searchClosingIndex; | ||
break; | ||
} | ||
} | ||
if (closingIndex === -1) { | ||
throw new Error("Invalid input: no closing parenthesis"); | ||
} | ||
number = MathCalculator.processFormulaWords(words, currentIndex + 1, closingIndex - 1); | ||
currentIndex = closingIndex; | ||
} | ||
else { | ||
number = parseFloat(word); | ||
} | ||
if (currentOperator === null) { | ||
if (result !== null) { | ||
throw new Error("Invalid input: operator expected"); | ||
} | ||
result = number; | ||
} | ||
else if (currentOperator === "sqrt") { | ||
result = Math.sqrt(number); | ||
} | ||
else if (result !== null) { | ||
switch (currentOperator) { | ||
case "+": | ||
result += number; | ||
break; | ||
case "-": | ||
result -= number; | ||
break; | ||
case "*": | ||
result *= number; | ||
break; | ||
case "/": | ||
if (number === 0) | ||
throw new Error("Division by zero"); | ||
result /= number; | ||
break; | ||
case "^": | ||
result = Math.pow(result, number); | ||
break; | ||
case "%": | ||
result %= number; | ||
break; | ||
case "sqrt": | ||
result = Math.sqrt(number); | ||
break; | ||
default: | ||
throw new Error(`Invalid operator: '${currentOperator}'`); | ||
} | ||
} | ||
else { | ||
throw new Error("Invalid input: no result available"); | ||
} | ||
currentOperator = null; | ||
} | ||
else if (["+", "-", "*", "/", "^", "%", "sqrt"].includes(word)) { | ||
if (currentOperator !== null) { | ||
throw new Error(`Invalid input: operator '${currentOperator}' already set`); | ||
} | ||
currentOperator = word; | ||
} | ||
else { | ||
throw new Error(`Invalid input: '${word}' is not a number or operator`); | ||
} | ||
} | ||
if (result === null) { | ||
throw new Error("Invalid input: no result computed"); | ||
} | ||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// MathCalculator.ts | ||
// tsc MathCalculator.ts -t esnext | ||
|
||
export class MathCalculator { | ||
|
||
static processFormulaVerbose(line: string): void { | ||
try { | ||
const result = MathCalculator.processFormulaString(line); | ||
console.log(`The result of '${line}' is '${result}'`); | ||
} catch (error) { | ||
console.error(`Error: ${error.message}`); | ||
} | ||
} | ||
|
||
static processFormulaString(line: string): number { | ||
const words = line.split(" "); | ||
return MathCalculator.processFormulaWords(words, 0, words.length - 1); | ||
} | ||
|
||
static processFormulaWords(words: string[], startIndex: number, endIndex: number): number { | ||
if (endIndex < startIndex) throw new RangeError("Invalid index: endIndex < startIndex"); | ||
|
||
let currentOperator: string | null = null; | ||
let result: number | null = null; | ||
|
||
for (let currentIndex = startIndex; currentIndex <= endIndex; currentIndex++) { | ||
const word = words[currentIndex]; | ||
const isNumber = !isNaN(parseFloat(word)); | ||
|
||
if (isNumber || word === "(") { | ||
let number: number; | ||
|
||
if (word === "(") { | ||
let depth = 0; | ||
let closingIndex = -1; | ||
|
||
for (let searchClosingIndex = currentIndex; searchClosingIndex <= endIndex; searchClosingIndex++) { | ||
if (words[searchClosingIndex] === "(") depth++; | ||
else if (words[searchClosingIndex] === ")") depth--; | ||
|
||
if (words[searchClosingIndex] === ")" && depth === 0) { | ||
closingIndex = searchClosingIndex; | ||
break; | ||
} | ||
} | ||
|
||
if (closingIndex === -1) { | ||
throw new Error("Invalid input: no closing parenthesis"); | ||
} | ||
|
||
number = MathCalculator.processFormulaWords(words, currentIndex + 1, closingIndex - 1); | ||
currentIndex = closingIndex; | ||
} else { | ||
number = parseFloat(word); | ||
} | ||
|
||
if (currentOperator === null) { | ||
if (result !== null) { | ||
throw new Error("Invalid input: operator expected"); | ||
} | ||
result = number; | ||
} else if (currentOperator === "sqrt") { | ||
result = Math.sqrt(number); | ||
} else if (result !== null) { | ||
switch (currentOperator) { | ||
case "+": | ||
result += number; | ||
break; | ||
case "-": | ||
result -= number; | ||
break; | ||
case "*": | ||
result *= number; | ||
break; | ||
case "/": | ||
if (number === 0) throw new Error("Division by zero"); | ||
result /= number; | ||
break; | ||
case "^": | ||
result = Math.pow(result, number); | ||
break; | ||
case "%": | ||
result %= number; | ||
break; | ||
case "sqrt": | ||
result = Math.sqrt(number); | ||
break; | ||
default: | ||
throw new Error(`Invalid operator: '${currentOperator}'`); | ||
} | ||
} else { | ||
throw new Error("Invalid input: no result available"); | ||
} | ||
|
||
currentOperator = null; | ||
} else if (["+", "-", "*", "/", "^", "%", "sqrt"].includes(word)) { | ||
if (currentOperator !== null) { | ||
throw new Error(`Invalid input: operator '${currentOperator}' already set`); | ||
} | ||
currentOperator = word; | ||
} else { | ||
throw new Error(`Invalid input: '${word}' is not a number or operator`); | ||
} | ||
} | ||
|
||
if (result === null) { | ||
throw new Error("Invalid input: no result computed"); | ||
} | ||
|
||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>math formula calculator</title> | ||
</head> | ||
|
||
<body> | ||
|
||
<h1>Math Formula Calculator</h1> | ||
<input type="text" id="formula" placeholder="Enter formula"> | ||
<button id="calculate">Calculate</button> | ||
<p id="result"></p> | ||
|
||
<script type="module"> | ||
|
||
//import { evaluate } from 'mathjs' | ||
|
||
// tsc MathCalculator.ts -t esnext | ||
import { MathCalculator } from './MathCalculator.js' | ||
|
||
function calculate() { | ||
var formula = document.getElementById('formula').value; | ||
var result = MathCalculator.processFormulaString(formula); | ||
document.getElementById('result').innerText = result; | ||
} | ||
|
||
document.getElementById('calculate').addEventListener('click', calculate); | ||
</script> | ||
</body> | ||
|
||
</html> |