1
|
|
|
<?php
|
2
|
|
|
|
3
|
|
|
namespace BCMathExtended;
|
4
|
|
|
|
5
|
|
|
/**
|
6
|
|
|
* Class BC
|
7
|
|
|
* @package BCMathExtended
|
8
|
|
|
*/
|
9
|
|
|
class BC
|
10
|
|
|
{
|
11
|
|
|
const COMPARE_EQUAL = 0;
|
12
|
|
|
const COMPARE_LEFT_GRATER = 1;
|
13
|
|
|
const COMPARE_RIGHT_GRATER = -1;
|
14
|
|
|
const DEFAULT_SCALE = 100;
|
15
|
|
|
|
16
|
|
|
/**
|
17
|
|
|
* @param null|int $scale
|
18
|
|
|
*/
|
19
|
323 |
|
public static function setScale($scale)
|
20
|
|
|
{
|
21
|
323 |
|
bcscale($scale);
|
22
|
323 |
|
}
|
23
|
|
|
|
24
|
|
|
/**
|
25
|
|
|
* @param int|string $number
|
26
|
|
|
* @param int $precision
|
27
|
|
|
* @return string
|
28
|
|
|
*/
|
29
|
47 |
|
public static function round($number, $precision = 0)
|
30
|
|
|
{
|
31
|
47 |
|
$number = self::convertScientificNotationToString($number);
|
32
|
47 |
|
if (self::checkIsFloat($number)) {
|
33
|
36 |
|
if (self::isNegative($number)) {
|
34
|
4 |
|
return self::sub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
|
35
|
|
|
}
|
36
|
|
|
|
37
|
32 |
|
return self::add($number, '0.' . str_repeat('0', $precision) . '5', $precision);
|
38
|
|
|
}
|
39
|
|
|
|
40
|
11 |
|
return self::checkNumber($number);
|
41
|
|
|
}
|
42
|
|
|
|
43
|
|
|
/**
|
44
|
|
|
* @param int|string|float $number
|
45
|
|
|
* @return string
|
46
|
|
|
*/
|
47
|
323 |
|
public static function convertScientificNotationToString($number)
|
48
|
|
|
{
|
49
|
|
|
// check if number is in scientific notation, first use stripos as is faster then preg_match
|
50
|
323 |
|
if (false !== stripos($number, 'E') && preg_match('/(-?(\d+\.)?\d+)E([+-]?)(\d+)/i', $number, $regs)) {
|
51
|
|
|
// calculate final scale of number
|
52
|
59 |
|
$scale = $regs[4] + self::getDecimalsLengthFromNumber($regs[1]);
|
53
|
59 |
|
$pow = self::pow(10, $regs[4], $scale);
|
54
|
59 |
|
if ('-' === $regs[3]) {
|
55
|
25 |
|
$number = self::div($regs[1], $pow, $scale);
|
56
|
|
|
} else {
|
57
|
34 |
|
$number = self::mul($pow, $regs[1], $scale);
|
58
|
|
|
}
|
59
|
|
|
// remove unnecessary 0 from 0.000 is a 0
|
60
|
59 |
|
$number = rtrim($number, '0');
|
61
|
|
|
// if you remove 0 you must clean dot
|
62
|
59 |
|
$number = rtrim($number, '.');
|
63
|
|
|
}
|
64
|
|
|
|
65
|
323 |
|
return self::checkNumber($number);
|
66
|
|
|
}
|
67
|
|
|
|
68
|
|
|
/**
|
69
|
|
|
* @param int|string|float $number
|
70
|
|
|
* @return int
|
71
|
|
|
*/
|
72
|
91 |
|
public static function getDecimalsLengthFromNumber($number)
|
73
|
|
|
{
|
74
|
91 |
|
$check = explode('.', $number);
|
75
|
91 |
|
if (!empty($check[1])) {
|
76
|
62 |
|
return strlen($check[1]);
|
77
|
|
|
}
|
78
|
|
|
|
79
|
29 |
|
return 0;
|
80
|
|
|
}
|
81
|
|
|
|
82
|
|
|
/**
|
83
|
|
|
* @param string $leftOperand
|
84
|
|
|
* @param string $rightOperand
|
85
|
|
|
* @param null|int $scale
|
86
|
|
|
* @return string
|
87
|
|
|
*/
|
88
|
111 |
|
public static function pow($leftOperand, $rightOperand, $scale = null)
|
89
|
|
|
{
|
90
|
111 |
|
$leftOperand = self::convertScientificNotationToString($leftOperand);
|
91
|
111 |
|
$rightOperand = self::convertScientificNotationToString($rightOperand);
|
92
|
|
|
|
93
|
111 |
|
if (self::checkIsFloat($rightOperand)) {
|
94
|
7 |
|
if (null === $scale) {
|
95
|
2 |
|
return self::powFractional($leftOperand, $rightOperand);
|
96
|
|
|
}
|
97
|
|
|
|
98
|
5 |
|
return self::powFractional($leftOperand, $rightOperand, $scale);
|
99
|
|
|
}
|
100
|
|
|
|
101
|
111 |
|
if (null === $scale) {
|
102
|
39 |
|
return bcpow($leftOperand, $rightOperand);
|
103
|
|
|
}
|
104
|
|
|
|
105
|
75 |
|
return bcpow($leftOperand, $rightOperand, $scale);
|
106
|
|
|
}
|
107
|
|
|
|
108
|
|
|
/**
|
109
|
|
|
* @param int|string $number
|
110
|
|
|
* @return bool
|
111
|
|
|
*/
|
112
|
221 |
|
private static function checkIsFloat($number)
|
113
|
|
|
{
|
114
|
221 |
|
return false !== strpos($number, '.');
|
115
|
|
|
}
|
116
|
|
|
|
117
|
|
|
/**
|
118
|
|
|
* @param string $leftOperand
|
119
|
|
|
* @param string $rightOperand
|
120
|
|
|
* @param null|int $scale
|
121
|
|
|
* @return string
|
122
|
|
|
*/
|
123
|
7 |
|
private static function powFractional($leftOperand, $rightOperand, $scale = null)
|
124
|
|
|
{
|
125
|
|
|
// we need to increased scale to get correct results and avoid rounding error
|
126
|
7 |
|
$increasedScale = null === $scale ? self::getScale() : $scale;
|
127
|
7 |
|
$increasedScale *= 2;
|
128
|
7 |
|
$decimals = explode('.', $rightOperand);
|
129
|
|
|
|
130
|
7 |
|
return self::checkNumber(
|
131
|
7 |
|
self::mul(
|
132
|
7 |
|
self::exp(
|
133
|
7 |
|
self::mul(
|
134
|
7 |
|
self::log($leftOperand),
|
135
|
7 |
|
'0.' . $decimals[1],
|
136
|
7 |
|
$increasedScale
|
137
|
|
|
)
|
138
|
|
|
),
|
139
|
7 |
|
self::pow($leftOperand, $decimals[0], $increasedScale),
|
140
|
7 |
|
$scale
|
141
|
|
|
)
|
142
|
|
|
);
|
143
|
|
|
}
|
144
|
|
|
|
145
|
|
|
/**
|
146
|
|
|
* @param int|string $number
|
147
|
|
|
* @return int|string
|
148
|
|
|
*/
|
149
|
323 |
|
private static function checkNumber($number)
|
150
|
|
|
{
|
151
|
323 |
|
$number = str_replace('+', '', filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
|
152
|
323 |
|
if ('-0' === $number || !is_numeric($number)) {
|
153
|
28 |
|
return '0';
|
154
|
|
|
}
|
155
|
|
|
|
156
|
320 |
|
return $number;
|
157
|
|
|
}
|
158
|
|
|
|
159
|
|
|
/**
|
160
|
|
|
* @param string $leftOperand
|
161
|
|
|
* @param string $rightOperand
|
162
|
|
|
* @param null|int $scale
|
163
|
|
|
* @return string
|
164
|
|
|
*/
|
165
|
131 |
View Code Duplication |
public static function mul($leftOperand, $rightOperand, $scale = null)
|
166
|
|
|
{
|
167
|
131 |
|
$leftOperand = self::convertScientificNotationToString($leftOperand);
|
168
|
131 |
|
$rightOperand = self::convertScientificNotationToString($rightOperand);
|
169
|
|
|
|
170
|
131 |
|
if (null === $scale) {
|
171
|
25 |
|
return bcmul($leftOperand, $rightOperand);
|
172
|
|
|
}
|
173
|
|
|
|
174
|
108 |
|
return bcmul($leftOperand, $rightOperand, $scale);
|
175
|
|
|
}
|
176
|
|
|
|
177
|
|
|
/**
|
178
|
|
|
* @param string $arg
|
179
|
|
|
* @return string
|
180
|
|
|
*/
|
181
|
15 |
|
public static function exp($arg)
|
182
|
|
|
{
|
183
|
15 |
|
$scale = self::DEFAULT_SCALE;
|
184
|
15 |
|
$result = '1';
|
185
|
15 |
|
for ($i = 299; $i > 0; $i--) {
|
186
|
15 |
|
$result = self::add(self::mul(self::div($result, $i, $scale), $arg, $scale), 1, $scale);
|
187
|
|
|
}
|
188
|
|
|
|
189
|
15 |
|
return $result;
|
190
|
|
|
}
|
191
|
|
|
|
192
|
|
|
/**
|
193
|
|
|
* @param string $leftOperand
|
194
|
|
|
* @param string $rightOperand
|
195
|
|
|
* @param null|int $scale
|
196
|
|
|
* @return string
|
197
|
|
|
*/
|
198
|
125 |
View Code Duplication |
public static function add($leftOperand, $rightOperand, $scale = null)
|
199
|
|
|
{
|
200
|
125 |
|
$leftOperand = self::convertScientificNotationToString($leftOperand);
|
201
|
125 |
|
$rightOperand = self::convertScientificNotationToString($rightOperand);
|
202
|
|
|
|
203
|
125 |
|
if (null === $scale) {
|
204
|
6 |
|
return bcadd($leftOperand, $rightOperand);
|
205
|
|
|
}
|
206
|
|
|
|
207
|
122 |
|
return bcadd($leftOperand, $rightOperand, $scale);
|
208
|
|
|
}
|
209
|
|
|
|
210
|
|
|
/**
|
211
|
|
|
* @param string $leftOperand
|
212
|
|
|
* @param string $rightOperand
|
213
|
|
|
* @param null|int $scale
|
214
|
|
|
* @return string
|
215
|
|
|
*/
|
216
|
97 |
View Code Duplication |
public static function div($leftOperand, $rightOperand, $scale = null)
|
217
|
|
|
{
|
218
|
97 |
|
$leftOperand = self::convertScientificNotationToString($leftOperand);
|
219
|
97 |
|
$rightOperand = self::convertScientificNotationToString($rightOperand);
|
220
|
|
|
|
221
|
97 |
|
if (null === $scale) {
|
222
|
12 |
|
return bcdiv($leftOperand, $rightOperand);
|
223
|
|
|
}
|
224
|
|
|
|
225
|
93 |
|
return bcdiv($leftOperand, $rightOperand, $scale);
|
226
|
|
|
}
|
227
|
|
|
|
228
|
|
|
/**
|
229
|
|
|
* @param string $arg
|
230
|
|
|
* @return string
|
231
|
|
|
*/
|
232
|
12 |
|
public static function log($arg)
|
233
|
|
|
{
|
234
|
12 |
|
$arg = self::convertScientificNotationToString($arg);
|
235
|
12 |
|
if ($arg === '0') {
|
236
|
1 |
|
return '-INF';
|
237
|
|
|
}
|
238
|
11 |
|
if (self::COMPARE_RIGHT_GRATER === self::comp($arg, '0')) {
|
239
|
1 |
|
return 'NAN';
|
240
|
|
|
}
|
241
|
10 |
|
$scale = self::DEFAULT_SCALE;
|
242
|
10 |
|
$m = (string)log($arg);
|
243
|
10 |
|
$x = self::sub(self::div($arg, self::exp($m), $scale), '1', $scale);
|
244
|
10 |
|
$res = '0';
|
245
|
10 |
|
$pow = '1';
|
246
|
10 |
|
$i = 1;
|
247
|
|
|
do {
|
248
|
10 |
|
$pow = self::mul($pow, $x, $scale);
|
249
|
10 |
|
$sum = self::div($pow, $i, $scale);
|
250
|
10 |
|
if ($i % 2 === 1) {
|
251
|
10 |
|
$res = self::add($res, $sum, $scale);
|
252
|
|
|
} else {
|
253
|
9 |
|
$res = self::sub($res, $sum, $scale);
|
254
|
|
|
}
|
255
|
10 |
|
$i++;
|
256
|
10 |
|
} while (self::comp($sum, '0', $scale));
|
257
|
|
|
|
258
|
10 |
|
return self::add($res, $m, $scale);
|
259
|
|
|
}
|
260
|
|
|
|
261
|
|
|
/**
|
262
|
|
|
* @param string $leftOperand
|
263
|
|
|
* @param string $rightOperand
|
264
|
|
|
* @param null|int $scale
|
265
|
|
|
* @return int
|
266
|
|
|
*/
|
267
|
31 |
|
public static function comp($leftOperand, $rightOperand, $scale = null)
|
268
|
|
|
{
|
269
|
31 |
|
$leftOperand = self::convertScientificNotationToString($leftOperand);
|
270
|
31 |
|
$rightOperand = self::convertScientificNotationToString($rightOperand);
|
271
|
|
|
|
272
|
31 |
|
if (null === $scale) {
|
273
|
20 |
|
return bccomp($leftOperand, $rightOperand, max(strlen($leftOperand), strlen($rightOperand)));
|
274
|
|
|
}
|
275
|
|
|
|
276
|
21 |
|
return bccomp(
|
277
|
21 |
|
$leftOperand,
|
278
|
21 |
|
$rightOperand,
|
279
|
21 |
|
$scale
|
280
|
|
|
);
|
281
|
|
|
}
|
282
|
|
|
|
283
|
|
|
/**
|
284
|
|
|
* @param string $leftOperand
|
285
|
|
|
* @param string $rightOperand
|
286
|
|
|
* @param null|int $scale
|
287
|
|
|
* @return string
|
288
|
|
|
*/
|
289
|
45 |
View Code Duplication |
public static function sub($leftOperand, $rightOperand, $scale = null)
|
290
|
|
|
{
|
291
|
45 |
|
$leftOperand = self::convertScientificNotationToString($leftOperand);
|
292
|
45 |
|
$rightOperand = self::convertScientificNotationToString($rightOperand);
|
293
|
|
|
|
294
|
45 |
|
if (null === $scale) {
|
295
|
14 |
|
return bcsub($leftOperand, $rightOperand);
|
296
|
|
|
}
|
297
|
|
|
|
298
|
33 |
|
return bcsub($leftOperand, $rightOperand, $scale);
|
299
|
|
|
}
|
300
|
|
|
|
301
|
|
|
/**
|
302
|
|
|
* @param $number
|
303
|
|
|
* @return bool
|
304
|
|
|
*/
|
305
|
118 |
|
private static function isNegative($number)
|
306
|
|
|
{
|
307
|
118 |
|
return 0 === strncmp('-', $number, 1);
|
308
|
|
|
}
|
309
|
|
|
|
310
|
|
|
/**
|
311
|
|
|
* @param int|string $number
|
312
|
|
|
* @return string
|
313
|
|
|
*/
|
314
|
15 |
|
public static function abs($number)
|
315
|
|
|
{
|
316
|
15 |
|
$number = self::convertScientificNotationToString($number);
|
317
|
|
|
|
318
|
15 |
|
if (self::isNegative($number)) {
|
319
|
8 |
|
$number = (string)substr($number, 1);
|
320
|
|
|
}
|
321
|
|
|
|
322
|
15 |
|
return self::checkNumber($number);
|
323
|
|
|
}
|
324
|
|
|
|
325
|
|
|
/**
|
326
|
|
|
* @param int|string $min
|
327
|
|
|
* @param int|string $max
|
328
|
|
|
* @return string
|
329
|
|
|
*/
|
330
|
2 |
|
public static function rand($min, $max)
|
331
|
|
|
{
|
332
|
2 |
|
$max = self::convertScientificNotationToString($max);
|
333
|
2 |
|
$min = self::convertScientificNotationToString($min);
|
334
|
|
|
|
335
|
2 |
|
$difference = self::add(self::sub($max, $min), 1);
|
336
|
2 |
|
$randPercent = self::div(mt_rand(), mt_getrandmax(), 8);
|
337
|
|
|
|
338
|
2 |
|
return self::add($min, self::mul($difference, $randPercent, 8), 0);
|
339
|
|
|
}
|
340
|
|
|
|
341
|
|
|
/**
|
342
|
|
|
* @param array|int|string,...
|
343
|
|
|
* @return null|string
|
344
|
|
|
*/
|
345
|
1 |
View Code Duplication |
public static function max()
|
|
|
|
|
346
|
|
|
{
|
347
|
1 |
|
$max = null;
|
348
|
1 |
|
$args = func_get_args();
|
349
|
1 |
|
if (is_array($args[0])) {
|
350
|
1 |
|
$args = $args[0];
|
351
|
|
|
}
|
352
|
1 |
|
foreach ($args as $number) {
|
353
|
1 |
|
$number = self::convertScientificNotationToString($number);
|
354
|
1 |
|
if (null === $max) {
|
355
|
1 |
|
$max = $number;
|
356
|
1 |
|
} else if (self::comp($max, $number) === self::COMPARE_RIGHT_GRATER) {
|
357
|
1 |
|
$max = $number;
|
358
|
|
|
}
|
359
|
|
|
}
|
360
|
|
|
|
361
|
1 |
|
return $max;
|
362
|
|
|
}
|
363
|
|
|
|
364
|
|
|
/**
|
365
|
|
|
* @param array|int|string,...
|
366
|
|
|
* @return null|string
|
367
|
|
|
*/
|
368
|
1 |
View Code Duplication |
public static function min()
|
|
|
|
|
369
|
|
|
{
|
370
|
1 |
|
$min = null;
|
371
|
1 |
|
$args = func_get_args();
|
372
|
1 |
|
if (is_array($args[0])) {
|
373
|
1 |
|
$args = $args[0];
|
374
|
|
|
}
|
375
|
1 |
|
foreach ($args as $number) {
|
376
|
1 |
|
$number = self::convertScientificNotationToString($number);
|
377
|
1 |
|
if (null === $min) {
|
378
|
1 |
|
$min = $number;
|
379
|
1 |
|
} else if (self::comp($min, $number) === self::COMPARE_LEFT_GRATER) {
|
380
|
1 |
|
$min = $number;
|
381
|
|
|
}
|
382
|
|
|
}
|
383
|
|
|
|
384
|
1 |
|
return $min;
|
385
|
|
|
}
|
386
|
|
|
|
387
|
|
|
/**
|
388
|
|
|
* @param int|string $number
|
389
|
|
|
* @param int $precision
|
390
|
|
|
* @return string
|
391
|
|
|
*/
|
392
|
17 |
View Code Duplication |
public static function roundDown($number, $precision = 0)
|
|
|
|
|
393
|
|
|
{
|
394
|
17 |
|
$number = self::convertScientificNotationToString($number);
|
395
|
17 |
|
$multiply = self::pow(10, (string)abs($precision));
|
396
|
|
|
|
397
|
17 |
|
return $precision < 0 ?
|
398
|
4 |
|
self::mul(
|
399
|
4 |
|
self::floor(self::div($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
|
400
|
4 |
|
$precision
|
401
|
|
|
) :
|
402
|
13 |
|
self::div(
|
403
|
13 |
|
self::floor(self::mul($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
|
404
|
17 |
|
$precision
|
405
|
|
|
);
|
406
|
|
|
}
|
407
|
|
|
|
408
|
|
|
/**
|
409
|
|
|
* @param int|string $number
|
410
|
|
|
* @return string
|
411
|
|
|
*/
|
412
|
65 |
View Code Duplication |
public static function floor($number)
|
|
|
|
|
413
|
|
|
{
|
414
|
65 |
|
$number = self::convertScientificNotationToString($number);
|
415
|
65 |
|
if (self::checkIsFloat($number) && self::checkIsFloatCleanZeros($number)) {
|
416
|
38 |
|
$result = 0;
|
417
|
38 |
|
if (self::isNegative($number)) {
|
418
|
7 |
|
--$result;
|
419
|
|
|
}
|
420
|
38 |
|
$number = self::add($number, $result, 0);
|
421
|
|
|
}
|
422
|
|
|
|
423
|
65 |
|
return self::checkNumber($number);
|
424
|
|
|
}
|
425
|
|
|
|
426
|
|
|
/**
|
427
|
|
|
* @param int|string $number
|
428
|
|
|
* @return bool
|
429
|
|
|
*/
|
430
|
68 |
|
private static function checkIsFloatCleanZeros(&$number)
|
431
|
|
|
{
|
432
|
68 |
|
return false !== strpos($number = rtrim(rtrim($number, '0'), '.'), '.');
|
433
|
|
|
}
|
434
|
|
|
|
435
|
|
|
/**
|
436
|
|
|
* @param int|string $number
|
437
|
|
|
* @param int $precision
|
438
|
|
|
* @return string
|
439
|
|
|
*/
|
440
|
17 |
View Code Duplication |
public static function roundUp($number, $precision = 0)
|
|
|
|
|
441
|
|
|
{
|
442
|
17 |
|
$number = self::convertScientificNotationToString($number);
|
443
|
17 |
|
$multiply = self::pow(10, (string)abs($precision));
|
444
|
|
|
|
445
|
17 |
|
return $precision < 0 ?
|
446
|
4 |
|
self::mul(
|
447
|
4 |
|
self::ceil(self::div($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
|
448
|
4 |
|
$precision
|
449
|
|
|
) :
|
450
|
13 |
|
self::div(
|
451
|
13 |
|
self::ceil(self::mul($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
|
452
|
17 |
|
$precision
|
453
|
|
|
);
|
454
|
|
|
}
|
455
|
|
|
|
456
|
|
|
/**
|
457
|
|
|
* @param int|string $number
|
458
|
|
|
* @return string
|
459
|
|
|
*/
|
460
|
41 |
View Code Duplication |
public static function ceil($number)
|
|
|
|
|
461
|
|
|
{
|
462
|
41 |
|
$number = self::convertScientificNotationToString($number);
|
463
|
41 |
|
if (self::checkIsFloat($number) && self::checkIsFloatCleanZeros($number)) {
|
464
|
22 |
|
$result = 1;
|
465
|
22 |
|
if (self::isNegative($number)) {
|
466
|
5 |
|
--$result;
|
467
|
|
|
}
|
468
|
22 |
|
$number = self::add($number, $result, 0);
|
469
|
|
|
}
|
470
|
|
|
|
471
|
41 |
|
return self::checkNumber($number);
|
472
|
|
|
}
|
473
|
|
|
|
474
|
|
|
/**
|
475
|
|
|
* @return int
|
476
|
|
|
*/
|
477
|
5 |
|
public static function getScale()
|
478
|
|
|
{
|
479
|
5 |
|
$sqrt = self::sqrt('2');
|
480
|
|
|
|
481
|
5 |
|
return strlen(substr($sqrt, strpos($sqrt, '.') + 1));
|
482
|
|
|
}
|
483
|
|
|
|
484
|
|
|
/**
|
485
|
|
|
* @param string $operand
|
486
|
|
|
* @param null|int $scale
|
487
|
|
|
* @return string
|
488
|
|
|
*/
|
489
|
10 |
|
public static function sqrt($operand, $scale = null)
|
490
|
|
|
{
|
491
|
10 |
|
$operand = self::convertScientificNotationToString($operand);
|
492
|
|
|
|
493
|
10 |
|
if (null === $scale) {
|
494
|
6 |
|
return bcsqrt($operand);
|
495
|
|
|
}
|
496
|
|
|
|
497
|
5 |
|
return bcsqrt($operand, $scale);
|
498
|
|
|
}
|
499
|
|
|
|
500
|
|
|
/**
|
501
|
|
|
* @param string $leftOperand
|
502
|
|
|
* @param string $modulus
|
503
|
|
|
* @param null $scale
|
504
|
|
|
* @return string
|
505
|
|
|
*/
|
506
|
23 |
|
public static function mod($leftOperand, $modulus, $scale = null)
|
507
|
|
|
{
|
508
|
23 |
|
$leftOperand = self::convertScientificNotationToString($leftOperand);
|
509
|
|
|
|
510
|
|
|
// bcmod in 7.2 is not working properly - for example bcmod(9.9999E-10, -0.00056, 9) should return '-0.000559999' but returns 0.0000000
|
511
|
|
|
|
512
|
|
|
// bcmod in php 5.6< don't support scale and floats
|
513
|
|
|
// let use this $x - floor($x/$y) * $y;
|
514
|
23 |
|
if (null === $scale) {
|
515
|
11 |
|
return self::sub($leftOperand, self::mul(self::floor(self::div($leftOperand, $modulus)), $modulus));
|
516
|
|
|
}
|
517
|
|
|
|
518
|
12 |
|
return self::sub(
|
519
|
12 |
|
$leftOperand, self::mul(self::floor(self::div($leftOperand, $modulus, $scale)), $modulus, $scale), $scale
|
520
|
|
|
);
|
521
|
|
|
}
|
522
|
|
|
|
523
|
|
|
/**
|
524
|
|
|
* @param string $leftOperand
|
525
|
|
|
* @param string $rightOperand
|
526
|
|
|
* @param string $modulus
|
527
|
|
|
* @param null|int $scale
|
528
|
|
|
* @return string
|
529
|
|
|
*/
|
530
|
10 |
|
public static function powMod($leftOperand, $rightOperand, $modulus, $scale = null)
|
531
|
|
|
{
|
532
|
10 |
|
$leftOperand = self::convertScientificNotationToString($leftOperand);
|
533
|
10 |
|
$rightOperand = self::convertScientificNotationToString($rightOperand);
|
534
|
|
|
|
535
|
|
|
// bcpowmod in 5.6 have don't calculate correct results if scale is empty
|
536
|
10 |
|
if (null === $scale) {
|
537
|
4 |
|
return self::mod(self::pow($leftOperand, $rightOperand), $modulus);
|
538
|
|
|
}
|
539
|
|
|
|
540
|
|
|
// cant use bcpowmod here as it don't support floats
|
541
|
6 |
|
if (self::checkIsFloat($leftOperand) || self::checkIsFloat($rightOperand) || self::checkIsFloat($modulus)) {
|
542
|
2 |
|
return self::mod(self::pow($leftOperand, $rightOperand, $scale), $modulus, $scale);
|
543
|
|
|
}
|
544
|
|
|
|
545
|
4 |
|
return bcpowmod($leftOperand, $rightOperand, $modulus, $scale);
|
546
|
|
|
}
|
547
|
|
|
|
548
|
|
|
/**
|
549
|
|
|
* @param string $arg
|
550
|
|
|
* @return string
|
551
|
|
|
* @throws \InvalidArgumentException
|
552
|
|
|
*/
|
553
|
8 |
|
public static function fact($arg)
|
554
|
|
|
{
|
555
|
8 |
|
$arg = self::convertScientificNotationToString($arg);
|
556
|
|
|
|
557
|
8 |
|
if (self::checkIsFloat($arg)) {
|
558
|
1 |
|
throw new \InvalidArgumentException('Number has to be an integer');
|
559
|
|
|
}
|
560
|
7 |
|
if (self::isNegative($arg)) {
|
561
|
1 |
|
throw new \InvalidArgumentException('Number has to be greater than or equal to 0');
|
562
|
|
|
}
|
563
|
|
|
|
564
|
6 |
|
$return = '1';
|
565
|
6 |
|
for ($i = 2; $i <= $arg; ++$i) {
|
566
|
5 |
|
$return = self::mul($return, $i);
|
567
|
|
|
}
|
568
|
|
|
|
569
|
6 |
|
return $return;
|
570
|
|
|
}
|
571
|
|
|
|
572
|
|
|
/**
|
573
|
|
|
* @param string $hex
|
574
|
|
|
* @return string
|
575
|
|
|
*/
|
576
|
5 |
|
public static function hexdec($hex) {
|
577
|
5 |
|
$remainingDigits = substr($hex, 0, -1);
|
578
|
5 |
|
$lastDigitToDecimal = \hexdec(substr($hex, -1));
|
579
|
|
|
|
580
|
5 |
|
if (strlen($remainingDigits) === 0) {
|
581
|
5 |
|
return $lastDigitToDecimal;
|
582
|
|
|
}
|
583
|
|
|
|
584
|
5 |
|
return self::add(self::mul(16, self::hexdec($remainingDigits)), $lastDigitToDecimal, 0);
|
585
|
|
|
}
|
586
|
|
|
|
587
|
|
|
/**
|
588
|
|
|
* @param int $decimal
|
589
|
|
|
* @return string
|
590
|
|
|
*/
|
591
|
6 |
|
public static function dechex($decimal) {
|
592
|
6 |
|
$quotient = self::div($decimal, 16, 0);
|
593
|
6 |
|
$remainderToHex = \dechex(self::mod($decimal, 16));
|
594
|
|
|
|
595
|
6 |
|
if (self::comp($quotient, 0) === 0) {
|
596
|
6 |
|
return $remainderToHex;
|
597
|
|
|
}
|
598
|
|
|
|
599
|
6 |
|
return self::dechex($quotient) . $remainderToHex;
|
600
|
|
|
}
|
601
|
|
|
}
|
602
|
|
|
|
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.