Completed
Push — master ( a30a03...e7dbd9 )
by kacper
02:25
created

BC::getDecimalsLengthFromNumber()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
cc 2
eloc 5
nc 2
nop 1
crap 2
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 190
    public static function convertScientificNotationToString($number)
47
    {
48
        // check if number is in scientific notation, first use stripos as is faster then preg_match
49 190
        if (false !== stripos($number, 'E') && preg_match('/(-?\d+\.\d+)E([+|-])(\d+)/i', $number, $regs)) {
50
            // calculate final scale of number
51 38
            $scale = $regs[3] + self::getDecimalsLengthFromNumber($regs[1]);
52 38
            $pow = self::pow(10, $regs[3], $regs[3]);
53 38
            if ('+' === $regs[2]) {
54 17
                $number = self::mul($pow, $regs[1], $scale);
55 21
            } else if ('-' === $regs[2]) {
56 21
                $number = self::div($regs[1], $pow, $scale);
57
            }
58
            // remove unnecessary 0 from 0.000 is a 0
59 38
            $number = rtrim($number, '0');
60
            // if you remove 0 you must clean dot
61 38
            $number = rtrim($number, '.');
62
        }
63
64 190
        return self::checkNumber($number);
65
    }
66
67
    /**
68
     * @param int|string|float $number
69
     * @return int
70
     */
71 38
    private static function getDecimalsLengthFromNumber($number)
72
    {
73 38
        $check = explode('.', $number);
74 38
        if (!empty($check[1])) {
75 21
            return strlen($check[1]);
76
        }
77
78 19
        return 0;
79
    }
80
81
82
    /**
83
     * @param string $leftOperand
84
     * @param string $rightOperand
85
     * @param int $scale
86
     * @return string
87
     */
88 39 View Code Duplication
    public static function mul($leftOperand, $rightOperand, $scale = null)
89
    {
90 39
        $leftOperand = self::convertScientificNotationToString($leftOperand);
91 39
        $rightOperand = self::convertScientificNotationToString($rightOperand);
92
93 39
        if (null === $scale) {
94 5
            return bcmul($leftOperand, $rightOperand);
95
        }
96
97 36
        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 43 View Code Duplication
    public static function pow($leftOperand, $rightOperand, $scale = null)
107
    {
108 43
        $leftOperand = self::convertScientificNotationToString($leftOperand);
109 43
        $rightOperand = self::convertScientificNotationToString($rightOperand);
110
111 43
        if (null === $scale) {
112 4
            return bcpow($leftOperand, $rightOperand);
113
        }
114
115 42
        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 26 View Code Duplication
    public static function div($leftOperand, $rightOperand, $scale = null)
125
    {
126 26
        $leftOperand = self::convertScientificNotationToString($leftOperand);
127 26
        $rightOperand = self::convertScientificNotationToString($rightOperand);
128
129 26
        if (null === $scale) {
130 3
            return bcdiv($leftOperand, $rightOperand);
131
        }
132
133 26
        return bcdiv($leftOperand, $rightOperand, $scale);
134
    }
135
136
    /**
137
     * @param int|string $number
138
     * @return int|string
139
     */
140 190
    private static function checkNumber($number)
141
    {
142 190
        $number = str_replace('+', '', filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
143 190
        if ('-0' === $number || !is_numeric($number)) {
144 20
            return '0';
145
        }
146
147 188
        return $number;
148
    }
149
150
    /**
151
     * @param int|string $number
152
     * @return bool
153
     */
154 101
    private static function checkIsFloat($number)
155
    {
156 101
        return false !== strpos($number, '.');
157
    }
158
159
    /**
160
     * @param $number
161
     * @return bool
162
     */
163 77
    private static function isNegative($number)
164
    {
165 77
        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 11 View Code Duplication
    public static function sub($leftOperand, $rightOperand, $scale = null)
175
    {
176 11
        $leftOperand = self::convertScientificNotationToString($leftOperand);
177 11
        $rightOperand = self::convertScientificNotationToString($rightOperand);
178
179 11
        if (null === $scale) {
180 5
            return bcsub($leftOperand, $rightOperand);
181
        }
182
183 9
        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 65 View Code Duplication
    public static function add($leftOperand, $rightOperand, $scale = null)
193
    {
194 65
        $leftOperand = self::convertScientificNotationToString($leftOperand);
195 65
        $rightOperand = self::convertScientificNotationToString($rightOperand);
196
197 65
        if (null === $scale) {
198 6
            return bcadd($leftOperand, $rightOperand);
199
        }
200
201 62
        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 1 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 1
        $number = self::convertScientificNotationToString($number);
311 1
        $multiply = self::pow(10, (string)abs($precision));
312
313 1
        return $precision < 0 ?
314 1
            self::mul(
315 1
                self::floor(self::div($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
316 1
                $precision
317
            ) :
318 1
            self::div(
319 1
                self::floor(self::mul($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
320 1
                $precision
321
            );
322
    }
323
324
    /**
325
     * @param int|string $number
326
     * @return string
327
     */
328 29 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 29
        $number = self::convertScientificNotationToString($number);
331 29
        if (self::checkIsFloat($number) && self::checkIsFloatCleanZeros($number)) {
332 15
            $result = 0;
333 15
            if (self::isNegative($number)) {
334 6
                --$result;
335
            }
336 15
            $number = self::add($number, $result, 0);
337
        }
338
339 29
        return self::checkNumber($number);
340
    }
341
342
    /**
343
     * @param int|string $number
344
     * @return bool
345
     */
346 28
    private static function checkIsFloatCleanZeros(&$number)
347
    {
348 28
        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 1 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 1
        $number = self::convertScientificNotationToString($number);
359 1
        $multiply = self::pow(10, (string)abs($precision));
360
361 1
        return $precision < 0 ?
362 1
            self::mul(
363 1
                self::ceil(self::div($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
364 1
                $precision
365
            ) :
366 1
            self::div(
367 1
                self::ceil(self::mul($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
368 1
                $precision
369
            );
370
    }
371
372
    /**
373
     * @param int|string $number
374
     * @return string
375
     */
376 25 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 25
        $number = self::convertScientificNotationToString($number);
379 25
        if (self::checkIsFloat($number) && self::checkIsFloatCleanZeros($number)) {
380 11
            $result = 1;
381 11
            if (self::isNegative($number)) {
382 4
                --$result;
383
            }
384 11
            $number = self::add($number, $result, 0);
385
        }
386
387 25
        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 5
    public static function sqrt($operand, $scale = null)
406
    {
407 5
        $operand = self::convertScientificNotationToString($operand);
408
409 5
        if (null === $scale) {
410 4
            return bcsqrt($operand);
411
        }
412
413 2
        return bcsqrt($operand, $scale);
414
    }
415
416
    /**
417
     * @param string $leftOperand
418
     * @param string $modulus
419
     * @param int $scale
420
     * @return string
421
     */
422 3
    public static function fmod($leftOperand, $modulus, $scale = null)
423
    {
424 3
        $leftOperand = self::convertScientificNotationToString($leftOperand);
425
426
        // From PHP 7.2 on, bcmod can handle real numbers
427 3
        if (version_compare(PHP_VERSION, '7.2.0') >= 0) {
428
            return self::mod($leftOperand, $modulus);
429
        }
430
431
        // mod(a, b) = a - b * floor(a/b)
432 3
        return self::sub(
433 3
            $leftOperand,
434 3
            self::mul(
435 3
                $modulus,
436 3
                self::floor(self::div($leftOperand, $modulus, $scale)),
437 3
                $scale
438
            ),
439 3
            $scale
440
        );
441
    }
442
443
    /**
444
     * @param string $leftOperand
445
     * @param string $modulus
446
     * @return string
447
     */
448 1
    public static function mod($leftOperand, $modulus)
449
    {
450 1
        return bcmod(
451 1
            self::convertScientificNotationToString($leftOperand),
452 1
            $modulus
453
        );
454
    }
455
456
    /**
457
     * @param string $leftOperand
458
     * @param string $rightOperand
459
     * @param string $modulus
460
     * @param int $scale
461
     * @return string
462
     */
463 1 View Code Duplication
    public static function powMod($leftOperand, $rightOperand, $modulus, $scale = null)
464
    {
465 1
        $leftOperand = self::convertScientificNotationToString($leftOperand);
466 1
        $rightOperand = self::convertScientificNotationToString($rightOperand);
467
468 1
        if (null === $scale) {
469
            return bcpowmod($leftOperand, $rightOperand, $modulus);
470
        }
471
472 1
        return bcpowmod($leftOperand, $rightOperand, $modulus, $scale);
473
    }
474
}
475