1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace DaveChild\TextStatistics; |
4
|
|
|
|
5
|
|
|
class Maths |
6
|
|
|
{ |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* @var boolean $blnBcmath Efficiency: Is the BC Math extension loaded? |
10
|
|
|
*/ |
11
|
|
|
protected static $blnBcmath = null; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Normalises score according to min & max allowed. If score larger |
15
|
|
|
* than max, max is returned. If score less than min, min is returned. |
16
|
|
|
* Also rounds result to specified precision. |
17
|
|
|
* Thanks to github.com/lvil. |
18
|
|
|
* @param int|float $score Initial score |
19
|
|
|
* @param int $min Minimum score allowed |
20
|
|
|
* @param int $max Maximum score allowed |
21
|
|
|
* @return int|float |
22
|
|
|
*/ |
23
|
1 |
|
public static function normaliseScore($score, $min, $max, $dps = 1) |
24
|
|
|
{ |
25
|
1 |
|
if ($score > $max) { |
26
|
1 |
|
$score = $max; |
27
|
1 |
|
} elseif ($score < $min) { |
28
|
1 |
|
$score = $min; |
29
|
1 |
|
} |
30
|
1 |
|
$score = self::bcCalc($score, '+', 0, true, $dps); // Round |
31
|
|
|
|
32
|
1 |
|
return $score; |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Do simple reliable floating point calculations without the risk of wrong results |
37
|
|
|
* @see http://floating-point-gui.de/ |
38
|
|
|
* @see the big red warning on http://php.net/language.types.float.php |
39
|
|
|
* |
40
|
|
|
* @source https://gist.github.com/jrfnl/8449978 |
41
|
|
|
* |
42
|
|
|
* In the rare case that the bcmath extension would not be loaded, it will return the normal calculation results |
43
|
|
|
* |
44
|
|
|
* @param mixed $number1 Scalar (string/int/float/bool) |
45
|
|
|
* @param string $action Calculation action to execute. Valid input: |
46
|
|
|
* '+' or 'add' or 'addition', |
47
|
|
|
* '-' or 'sub' or 'subtract', |
48
|
|
|
* '*' or 'mul' or 'multiply', |
49
|
|
|
* '/' or 'div' or 'divide', |
50
|
|
|
* '%' or 'mod' or 'modulus' |
51
|
|
|
* '=' or 'comp' or 'compare' |
52
|
|
|
* @param mixed $number2 Scalar (string/int/float/bool) |
53
|
|
|
* @param bool $round Whether or not to round the result. Defaults to false. |
54
|
|
|
* Will be disregarded for a compare operation |
55
|
|
|
* @param int $decimals Decimals for rounding operation. Defaults to 0. |
56
|
|
|
* @param int $precision Calculation precision. Defaults to 10. |
57
|
|
|
* @return mixed Calculation result or false if either or the numbers isn't scalar or |
58
|
|
|
* an invalid operation was passed |
59
|
|
|
* - for compare the result will always be an integer |
60
|
|
|
* - for all other operations, the result will either be an integer |
61
|
|
|
* (preferred) or a float |
62
|
|
|
*/ |
63
|
23 |
|
public static function bcCalc($number1, $action, $number2, $round = false, $decimals = 0, $precision = 10) |
64
|
|
|
{ |
65
|
23 |
|
if (!is_scalar($number1) || !is_scalar($number2)) { |
66
|
1 |
|
return false; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
// Check whether bcmath extension is available |
70
|
23 |
|
if (is_null(self::$blnBcmath)) { |
71
|
1 |
|
self::$blnBcmath = extension_loaded('bcmath'); |
72
|
1 |
|
} |
73
|
|
|
|
74
|
|
|
// Check values of input variables |
75
|
23 |
|
if (self::$blnBcmath) { |
76
|
23 |
|
$number1 = strval($number1); |
77
|
23 |
|
$number2 = strval($number2); |
78
|
23 |
|
} |
79
|
|
|
|
80
|
|
|
// Normalise operator |
81
|
23 |
|
$action = self::normaliseOperator($action); |
82
|
|
|
|
83
|
|
|
// Perform calculation |
84
|
23 |
|
return self::performCalc($number1, $action, $number2, $round, $decimals, $precision); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Normalise operators for bcMath function. |
89
|
|
|
* @param string $operator Operators such as "+", "add" |
90
|
|
|
* @return string |
91
|
|
|
*/ |
92
|
23 |
|
public static function normaliseOperator($operator) |
93
|
|
|
{ |
94
|
|
|
switch ($operator) { |
95
|
23 |
|
case 'add': |
96
|
23 |
|
case 'addition': |
97
|
1 |
|
$operator = '+'; |
98
|
1 |
|
break; |
99
|
23 |
|
case 'sub': |
100
|
23 |
|
case 'subtract': |
101
|
1 |
|
$operator = '-'; |
102
|
1 |
|
break; |
103
|
23 |
|
case 'mul': |
104
|
23 |
|
case 'multiply': |
105
|
1 |
|
$operator = '*'; |
106
|
1 |
|
break; |
107
|
23 |
|
case 'div': |
108
|
23 |
|
case 'divide': |
109
|
1 |
|
$operator = '/'; |
110
|
1 |
|
break; |
111
|
23 |
|
case 'mod': |
112
|
23 |
|
case 'modulus': |
113
|
1 |
|
$operator = '%'; |
114
|
1 |
|
break; |
115
|
23 |
|
case 'comp': |
116
|
23 |
|
case 'compare': |
117
|
1 |
|
$operator = '='; |
118
|
1 |
|
break; |
119
|
|
|
} |
120
|
23 |
|
return $operator; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Function which performs calculation. |
125
|
|
|
* @param string|integer|float|boolean $number1 See bcCalc description |
126
|
|
|
* @param string $action See bcCalc description |
127
|
|
|
* @param string|integer|float|boolean $number2 See bcCalc description |
128
|
|
|
* @param boolean $round See bcCalc description |
129
|
|
|
* @param integer $decimals See bcCalc description |
130
|
|
|
* @param integer $precision See bcCalc description |
131
|
|
|
* @return integer|float|boolean |
132
|
|
|
*/ |
133
|
23 |
|
private static function performCalc($number1, $action, $number2, $round, $decimals, $precision) |
134
|
|
|
{ |
135
|
23 |
|
$result = null; |
136
|
23 |
|
$compare = false; |
137
|
|
|
switch ($action) { |
138
|
23 |
View Code Duplication |
case '+': |
|
|
|
|
139
|
20 |
|
$result = (self::$blnBcmath) ? bcadd($number1, $number2, $precision) /* string */ : ($number1 + $number2); |
140
|
20 |
|
break; |
141
|
22 |
|
case '-': |
142
|
13 |
|
$result = (self::$blnBcmath) ? bcsub($number1, $number2, $precision) /* string */ : ($number1 - $number2); |
143
|
13 |
|
break; |
144
|
22 |
View Code Duplication |
case '*': |
|
|
|
|
145
|
20 |
|
$result = (self::$blnBcmath) ? bcmul($number1, $number2, $precision) /* string */ : ($number1 * $number2); |
146
|
20 |
|
break; |
147
|
22 |
|
case 'sqrt': |
148
|
4 |
|
$result = (self::$blnBcmath) ? bcsqrt($number1, $precision) /* string */ : sqrt($number1); |
149
|
4 |
|
break; |
150
|
22 |
View Code Duplication |
case '/': |
|
|
|
|
151
|
22 |
|
if ($number2 > 0) { |
152
|
22 |
|
if (self::$blnBcmath) { |
153
|
22 |
|
$result = bcdiv($number1, $number2, $precision); // string, or NULL if right_operand is 0 |
154
|
22 |
|
} else if ($number2 != 0) { |
155
|
|
|
$result = $number1 / $number2; |
156
|
|
|
} |
157
|
22 |
|
} |
158
|
|
|
|
159
|
22 |
|
if (!isset($result)) { |
160
|
1 |
|
$result = 0; |
161
|
1 |
|
} |
162
|
22 |
|
break; |
163
|
1 |
View Code Duplication |
case '%': |
|
|
|
|
164
|
1 |
|
if (self::$blnBcmath) { |
165
|
1 |
|
$result = bcmod($number1, $number2); // string, or NULL if modulus is 0. |
166
|
1 |
|
} else if ($number2 != 0) { |
167
|
|
|
$result = $number1 % $number2; |
168
|
|
|
} |
169
|
|
|
|
170
|
1 |
|
if (!isset($result)) { |
171
|
|
|
$result = 0; |
172
|
|
|
} |
173
|
1 |
|
break; |
174
|
1 |
|
case '=': |
175
|
1 |
|
$compare = true; |
176
|
1 |
|
if (self::$blnBcmath) { |
177
|
1 |
|
$result = bccomp($number1, $number2, $precision); // returns int 0, 1 or -1 |
178
|
1 |
|
} else { |
179
|
|
|
$result = ($number1 == $number2) ? 0 : (($number1 > $number2) ? 1 : -1); |
180
|
|
|
} |
181
|
1 |
|
break; |
182
|
|
|
} |
183
|
|
|
|
184
|
23 |
|
if (isset($result)) { |
185
|
23 |
|
if ($compare === false) { |
186
|
23 |
|
if ($round === true) { |
187
|
20 |
|
$result = round(floatval($result), $decimals); |
188
|
20 |
|
if ($decimals === 0) { |
189
|
1 |
|
$result = (int) $result; |
190
|
1 |
|
} |
191
|
20 |
|
} else { |
192
|
22 |
|
$result = (intval($result) == $result) ? intval($result) : floatval($result); |
193
|
|
|
} |
194
|
23 |
|
} |
195
|
23 |
|
return $result; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
return false; |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.