-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMathCalculator.php
139 lines (121 loc) · 4.95 KB
/
MathCalculator.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<?php
// to run this script in the command line, use the following command:
// php MathCalculator.php
// example run :
// enter a math formula (with spaces) : 5 + 2 * 3
// The result of '5 + 2 * 3' is '21'
class MathCalculator
{
public static function processFormulaVerbose(string $line): void
{
try {
$result = self::processFormulaString($line);
echo "The result of '$line' is '$result'\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
}
public static function processFormulaString(string $line): float
{
$words = explode(" ", $line);
return self::processFormulaWords($words, 0, count($words) - 1);
}
public static function processFormulaWords(array $words, int $startIndex, int $endIndex): float
{
if ($endIndex < $startIndex) {
throw new RangeException("Invalid index: endIndex < startIndex");
}
$currentOperator = null;
$result = null;
for ($currentIndex = $startIndex; $currentIndex <= $endIndex; $currentIndex++) {
$word = $words[$currentIndex];
$isNumber = is_numeric($word);
if ($isNumber || $word === "(") {
if ($word === "(") {
$depth = 0;
$closingIndex = -1;
for ($searchClosingIndex = $currentIndex; $searchClosingIndex <= $endIndex; $searchClosingIndex++) {
if ($words[$searchClosingIndex] === "(") {
$depth++;
} elseif ($words[$searchClosingIndex] === ")") {
$depth--;
}
if ($words[$searchClosingIndex] === ")" && $depth === 0) {
$closingIndex = $searchClosingIndex;
break;
}
}
if ($closingIndex === -1) {
throw new InvalidArgumentException("Invalid input: no closing parenthesis");
}
$number = self::processFormulaWords($words, $currentIndex + 1, $closingIndex - 1);
$currentIndex = $closingIndex;
} else {
$number = (float)$word;
}
if ($currentOperator === null) {
if ($result !== null) {
throw new InvalidArgumentException("Invalid input: operator expected");
}
$result = $number;
} elseif ($currentOperator === "sqrt") {
$result = sqrt($number);
} elseif ($result !== null) {
switch ($currentOperator) {
case "+":
$result += $number;
break;
case "-":
$result -= $number;
break;
case "*":
$result *= $number;
break;
case "/":
if ($number === 0) {
throw new DivisionByZeroError("Division by zero");
}
$result /= $number;
break;
case "^":
$result = pow($result, $number);
break;
case "%":
$result %= $number;
break;
default:
throw new InvalidArgumentException("Invalid operator: '$currentOperator'");
}
} else {
throw new InvalidArgumentException("Invalid input: no result available");
}
$currentOperator = null;
} elseif (in_array($word, ["+", "-", "*", "/", "^", "%", "sqrt"], true)) {
if ($currentOperator !== null) {
throw new InvalidArgumentException("Invalid input: operator '$currentOperator' already set");
}
$currentOperator = $word;
} else {
throw new InvalidArgumentException("Invalid input: '$word' is not a number or operator");
}
}
if ($result === null) {
throw new InvalidArgumentException("Invalid input: no result computed");
}
return $result;
}
}
while (true) {
$ask = "enter a math formula (with spaces) : ";
if (function_exists('readline')) {
$line = readline('[readline] ' . $ask);
} else {
echo '[fgets] ' . $ask;
$line = trim(fgets(STDIN));
}
if ($line === '' || $line === null || $line === false || $line === "exit") {
break;
}
MathCalculator::processFormulaVerbose($line);
}
// php MathCalculator.php