Completed
Push — master ( 0e2dce...0e8e4b )
by kacper
03:24
created

BC::powMod()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 10
Ratio 90.91 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 10
loc 11
ccs 5
cts 6
cp 0.8333
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 4
crap 2.0185
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
15
    /**
16
     * @param int $scale
17
     */
18 13
    public static function setScale($scale)
19
    {
20 13
        bcscale($scale);
21 13
    }
22
23
    /**
24
     * @param int|string $number
25
     * @param int $precision
26
     * @return string
27
     */
28 47
    public static function round($number, $precision = 0)
29
    {
30 47
        $number = self::convertScientificNotationToString($number);
31 47
        if (self::checkIsFloat($number)) {
32 36
            if (self::isNegative($number)) {
33 4
                return self::sub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
34
            }
35
36 32
            return self::add($number, '0.' . str_repeat('0', $precision) . '5', $precision);
37
        }
38
39 11
        return self::checkNumber($number);
40
    }
41
42
    /**
43
     * @param int|string|float $number
44
     * @return string
45
     */
46 279
    public static function convertScientificNotationToString($number)
47
    {
48
        // check if number is in scientific notation, first use stripos as is faster then preg_match
49 279
        if (false !== stripos($number, 'E') && preg_match('/(-?(\d+\.)?\d+)E([+-]?)(\d+)/i', $number, $regs)) {
50
            // calculate final scale of number
51 59
            $scale = $regs[4] + self::getDecimalsLengthFromNumber($regs[1]);
52 59
            $pow = self::pow(10, $regs[4], $scale);
53 59
            if ('-' === $regs[3]) {
54 25
                $number = self::div($regs[1], $pow, $scale);
55
            } else {
56 34
                $number = self::mul($pow, $regs[1], $scale);
57
            }
58
            // remove unnecessary 0 from 0.000 is a 0
59 59
            $number = rtrim($number, '0');
60
            // if you remove 0 you must clean dot
61 59
            $number = rtrim($number, '.');
62
        }
63
64 279
        return self::checkNumber($number);
65
    }
66
67
    /**
68
     * @param int|string|float $number
69
     * @return int
70
     */
71 91
    public static function getDecimalsLengthFromNumber($number)
72
    {
73 91
        $check = explode('.', $number);
74 91
        if (!empty($check[1])) {
75 62
            return strlen($check[1]);
76
        }
77
78 29
        return 0;
79
    }
80
81
82
    /**
83
     * @param string $leftOperand
84
     * @param string $rightOperand
85
     * @param int $scale
86
     * @return string
87
     */
88 92 View Code Duplication
    public static function mul($leftOperand, $rightOperand, $scale = null)
89
    {
90 92
        $leftOperand = self::convertScientificNotationToString($leftOperand);
91 92
        $rightOperand = self::convertScientificNotationToString($rightOperand);
92
93 92
        if (null === $scale) {
94 5
            return bcmul($leftOperand, $rightOperand);
95
        }
96
97 89
        return bcmul($leftOperand, $rightOperand, $scale);
98
    }
99
100
    /**
101
     * @param string $leftOperand
102
     * @param string $rightOperand
103
     * @param int $scale
104
     * @return string
105
     */
106 96 View Code Duplication
    public static function pow($leftOperand, $rightOperand, $scale = null)
107
    {
108 96
        $leftOperand = self::convertScientificNotationToString($leftOperand);
109 96
        $rightOperand = self::convertScientificNotationToString($rightOperand);
110
111 96
        if (null === $scale) {
112 36
            return bcpow($leftOperand, $rightOperand);
113
        }
114
115 63
        return bcpow($leftOperand, $rightOperand, $scale);
116
    }
117
118
    /**
119
     * @param string $leftOperand
120
     * @param string $rightOperand
121
     * @param int $scale
122
     * @return string
123
     */
124 69 View Code Duplication
    public static function div($leftOperand, $rightOperand, $scale = null)
125
    {
126 69
        $leftOperand = self::convertScientificNotationToString($leftOperand);
127 69
        $rightOperand = self::convertScientificNotationToString($rightOperand);
128
129 69
        if (null === $scale) {
130 3
            return bcdiv($leftOperand, $rightOperand);
131
        }
132
133 69
        return bcdiv($leftOperand, $rightOperand, $scale);
134
    }
135
136
    /**
137
     * @param int|string $number
138
     * @return int|string
139
     */
140 279
    private static function checkNumber($number)
141
    {
142 279
        $number = str_replace('+', '', filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
143 279
        if ('-0' === $number || !is_numeric($number)) {
144 27
            return '0';
145
        }
146
147 277
        return $number;
148
    }
149
150
    /**
151
     * @param int|string $number
152
     * @return bool
153
     */
154 137
    private static function checkIsFloat($number)
155
    {
156 137
        return false !== strpos($number, '.');
157
    }
158
159
    /**
160
     * @param $number
161
     * @return bool
162
     */
163 99
    private static function isNegative($number)
164
    {
165 99
        return 0 === strncmp('-', $number, 1);
166
    }
167
168
    /**
169
     * @param string $leftOperand
170
     * @param string $rightOperand
171
     * @param int $scale
172
     * @return string
173
     */
174 21 View Code Duplication
    public static function sub($leftOperand, $rightOperand, $scale = null)
175
    {
176 21
        $leftOperand = self::convertScientificNotationToString($leftOperand);
177 21
        $rightOperand = self::convertScientificNotationToString($rightOperand);
178
179 21
        if (null === $scale) {
180 5
            return bcsub($leftOperand, $rightOperand);
181
        }
182
183 19
        return bcsub($leftOperand, $rightOperand, $scale);
184
    }
185
186
    /**
187
     * @param string $leftOperand
188
     * @param string $rightOperand
189
     * @param int $scale
190
     * @return string
191
     */
192 95 View Code Duplication
    public static function add($leftOperand, $rightOperand, $scale = null)
193
    {
194 95
        $leftOperand = self::convertScientificNotationToString($leftOperand);
195 95
        $rightOperand = self::convertScientificNotationToString($rightOperand);
196
197 95
        if (null === $scale) {
198 6
            return bcadd($leftOperand, $rightOperand);
199
        }
200
201 92
        return bcadd($leftOperand, $rightOperand, $scale);
202
    }
203
204
    /**
205
     * @param int|string $number
206
     * @return string
207
     */
208 15
    public static function abs($number)
209
    {
210 15
        $number = self::convertScientificNotationToString($number);
211
212 15
        if (self::isNegative($number)) {
213 8
            $number = (string)substr($number, 1);
214
        }
215
216 15
        return self::checkNumber($number);
217
    }
218
219
    /**
220
     * @param int|string $min
221
     * @param int|string $max
222
     * @return string
223
     */
224 2
    public static function rand($min, $max)
225
    {
226 2
        $max = self::convertScientificNotationToString($max);
227 2
        $min = self::convertScientificNotationToString($min);
228
229 2
        $difference = self::add(self::sub($max, $min), 1);
230 2
        $randPercent = self::div(mt_rand(), mt_getrandmax(), 8);
231
232 2
        return self::add($min, self::mul($difference, $randPercent, 8), 0);
233
    }
234
235
    /**
236
     * @param array|int|string,...
237
     * @return null|string
238
     */
239 1 View Code Duplication
    public static function max()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
240
    {
241 1
        $max = null;
242 1
        $args = func_get_args();
243 1
        if (is_array($args[0])) {
244 1
            $args = $args[0];
245
        }
246 1
        foreach ($args as $number) {
247 1
            $number = self::convertScientificNotationToString($number);
248 1
            if (null === $max) {
249 1
                $max = $number;
250 1
            } else if (self::comp($max, $number) === self::COMPARE_RIGHT_GRATER) {
251 1
                $max = $number;
252
            }
253
        }
254
255 1
        return $max;
256
    }
257
258
    /**
259
     * @param string $leftOperand
260
     * @param string $rightOperand
261
     * @param int $scale
262
     * @return int
263
     */
264 14
    public static function comp($leftOperand, $rightOperand, $scale = null)
265
    {
266 14
        $leftOperand = self::convertScientificNotationToString($leftOperand);
267 14
        $rightOperand = self::convertScientificNotationToString($rightOperand);
268
269 14
        if (null === $scale) {
270 3
            return bccomp($leftOperand, $rightOperand, max(strlen($leftOperand), strlen($rightOperand)));
271
        }
272
273 11
        return bccomp(
274 11
            $leftOperand,
275 11
            $rightOperand,
276 11
            $scale
277
        );
278
    }
279
280
    /**
281
     * @param array|int|string,...
282
     * @return null|string
283
     */
284 1 View Code Duplication
    public static function min()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
285
    {
286 1
        $min = null;
287 1
        $args = func_get_args();
288 1
        if (is_array($args[0])) {
289 1
            $args = $args[0];
290
        }
291 1
        foreach ($args as $number) {
292 1
            $number = self::convertScientificNotationToString($number);
293 1
            if (null === $min) {
294 1
                $min = $number;
295 1
            } else if (self::comp($min, $number) === self::COMPARE_LEFT_GRATER) {
296 1
                $min = $number;
297
            }
298
        }
299
300 1
        return $min;
301
    }
302
303
    /**
304
     * @param int|string $number
305
     * @param int $precision
306
     * @return string
307
     */
308 17 View Code Duplication
    public static function roundDown($number, $precision = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
309
    {
310 17
        $number = self::convertScientificNotationToString($number);
311 17
        $multiply = self::pow(10, (string)abs($precision));
312
313 17
        return $precision < 0 ?
314 4
            self::mul(
315 4
                self::floor(self::div($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
316 4
                $precision
317
            ) :
318 13
            self::div(
319 13
                self::floor(self::mul($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
320 17
                $precision
321
            );
322
    }
323
324
    /**
325
     * @param int|string $number
326
     * @return string
327
     */
328 49 View Code Duplication
    public static function floor($number)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
329
    {
330 49
        $number = self::convertScientificNotationToString($number);
331 49
        if (self::checkIsFloat($number) && self::checkIsFloatCleanZeros($number)) {
332 26
            $result = 0;
333 26
            if (self::isNegative($number)) {
334 7
                --$result;
335
            }
336 26
            $number = self::add($number, $result, 0);
337
        }
338
339 49
        return self::checkNumber($number);
340
    }
341
342
    /**
343
     * @param int|string $number
344
     * @return bool
345
     */
346 56
    private static function checkIsFloatCleanZeros(&$number)
347
    {
348 56
        return false !== strpos($number = rtrim(rtrim($number, '0'), '.'), '.');
349
    }
350
351
    /**
352
     * @param int|string $number
353
     * @param int $precision
354
     * @return string
355
     */
356 17 View Code Duplication
    public static function roundUp($number, $precision = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
357
    {
358 17
        $number = self::convertScientificNotationToString($number);
359 17
        $multiply = self::pow(10, (string)abs($precision));
360
361 17
        return $precision < 0 ?
362 4
            self::mul(
363 4
                self::ceil(self::div($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
364 4
                $precision
365
            ) :
366 13
            self::div(
367 13
                self::ceil(self::mul($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
368 17
                $precision
369
            );
370
    }
371
372
    /**
373
     * @param int|string $number
374
     * @return string
375
     */
376 41 View Code Duplication
    public static function ceil($number)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
377
    {
378 41
        $number = self::convertScientificNotationToString($number);
379 41
        if (self::checkIsFloat($number) && self::checkIsFloatCleanZeros($number)) {
380 22
            $result = 1;
381 22
            if (self::isNegative($number)) {
382 5
                --$result;
383
            }
384 22
            $number = self::add($number, $result, 0);
385
        }
386
387 41
        return self::checkNumber($number);
388
    }
389
390
    /**
391
     * @return int
392
     */
393 3
    public static function getScale()
394
    {
395 3
        $sqrt = self::sqrt('2');
396
397 3
        return strlen(substr($sqrt, strpos($sqrt, '.') + 1));
398
    }
399
400
    /**
401
     * @param string $operand
402
     * @param int $scale
403
     * @return string
404
     */
405 8
    public static function sqrt($operand, $scale = null)
406
    {
407 8
        $operand = self::convertScientificNotationToString($operand);
408
409 8
        if (null === $scale) {
410 4
            return bcsqrt($operand);
411
        }
412
413 5
        return bcsqrt($operand, $scale);
414
    }
415
416
    /**
417
     * @param string $leftOperand
418
     * @param string $modulus
419
     * @param int $scale
420
     * @return string
421
     */
422 7
    public static function fmod($leftOperand, $modulus, $scale = null)
423
    {
424 7
        $leftOperand = self::convertScientificNotationToString($leftOperand);
425
426
        // mod(a, b) = a - b * floor(a/b)
427 7
        return self::sub(
428 7
            $leftOperand,
429 7
            self::mul(
430 7
                $modulus,
431 7
                self::floor(self::div($leftOperand, $modulus, $scale)),
432 7
                $scale
433
            ),
434 7
            $scale
435
        );
436
    }
437
438
    /**
439
     * @param string $leftOperand
440
     * @param string $modulus
441
     * @return string
442
     */
443 4
    public static function mod($leftOperand, $modulus)
444
    {
445 4
        $leftOperand = self::convertScientificNotationToString($leftOperand);
446
447 4
        if (version_compare(PHP_VERSION, '7.2.0') >= 0) {
448
            return bcmod(
449
                $leftOperand,
450
                $modulus,
451
                0
452
            );
453
        }
454
455 4
        return bcmod(
456 4
            $leftOperand,
457 4
            $modulus
458
        );
459
    }
460
461
    /**
462
     * @param string $leftOperand
463
     * @param string $rightOperand
464
     * @param string $modulus
465
     * @param int $scale
466
     * @return string
467
     */
468 4 View Code Duplication
    public static function powMod($leftOperand, $rightOperand, $modulus, $scale = null)
469
    {
470 4
        $leftOperand = self::convertScientificNotationToString($leftOperand);
471 4
        $rightOperand = self::convertScientificNotationToString($rightOperand);
472
473 4
        if (null === $scale) {
474
            return bcpowmod($leftOperand, $rightOperand, $modulus);
475
        }
476
477 4
        return bcpowmod($leftOperand, $rightOperand, $modulus, $scale);
478
    }
479
}
480