Skip to content

Commit

Permalink
[TS] [ DHTML and TypeScript ] Add MathCalculator class for processing…
Browse files Browse the repository at this point in the history
… mathematical formulas with support for nested expressions and square root operation
  • Loading branch information
arkenidar committed Dec 23, 2024
1 parent 9bc13eb commit 793ee19
Show file tree
Hide file tree
Showing 3 changed files with 253 additions and 0 deletions.
107 changes: 107 additions & 0 deletions MathCalculator.js
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;
}
}
112 changes: 112 additions & 0 deletions MathCalculator.ts
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;
}
}
34 changes: 34 additions & 0 deletions index.html
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>

0 comments on commit 793ee19

Please sign in to comment.