Test Failed
Push — master ( 4c0154...c2ce66 )
by kacper
02:57
created

BC::trimTrailingZeroes()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 5
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 11
ccs 6
cts 6
cp 1
crap 4
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BCMathExtended;
6
7
use Closure;
8
use InvalidArgumentException;
9
10
class BC
11
{
12
    public const COMPARE_EQUAL = 0;
13
    public const COMPARE_LEFT_GRATER = 1;
14
    public const COMPARE_RIGHT_GRATER = -1;
15
16
    protected const DEFAULT_SCALE = 100;
17
18
    protected const MAX_BASE = 256;
19
20
    protected const BIT_OPERATOR_AND = 'and';
21
    protected const BIT_OPERATOR_OR = 'or';
22
    protected const BIT_OPERATOR_XOR = 'xor';
23
24
    protected static $trimTrailingZeroes = true;
25 56
26
    public static function rand(string $min, string $max): string
27 56
    {
28 56
        $max = static::convertScientificNotationToString($max);
29 45
        $min = static::convertScientificNotationToString($min);
30 9
31
        $difference = static::add(static::sub($max, $min), '1');
32
        $randPercent = static::div((string)mt_rand(), (string)mt_getrandmax(), 8);
33 36
34
        return static::add($min, static::mul($difference, $randPercent, 8), 0);
35
    }
36 11
37
    public static function convertScientificNotationToString(string $number): string
38
    {
39 391
        // check if number is in scientific notation, first use stripos as is faster then preg_match
40
        if (false !== stripos($number, 'E') && preg_match('/(-?(\d+\.)?\d+)E([+-]?)(\d+)/i', $number, $regs)) {
41
            // calculate final scale of number
42 391
            $scale = $regs[4] + static::getDecimalsLengthFromNumber($regs[1]);
43
            $pow = static::pow('10', $regs[4], $scale);
44 60
            if ('-' === $regs[3]) {
45 60
                $number = static::div($regs[1], $pow, $scale);
46 60
            } else {
47 25
                $number = static::mul($pow, $regs[1], $scale);
48
            }
49 35
            // remove unnecessary 0 and dot from 0.000 is a 0
50
            $number = static::trimTrailingZeroes($number);
51
        }
52 60
53
        return static::checkNumber($number);
54
    }
55 391
56
    public static function getDecimalsLengthFromNumber(string $number): int
57
    {
58 107
        $check = explode('.', $number);
59
        if (!empty($check[1])) {
60 107
            return strlen($check[1]);
61 107
        }
62 77
63
        return 0;
64
    }
65 30
66
    public static function pow(string $leftOperand, string $rightOperand, ?int $scale = null): string
67
    {
68 161
        $leftOperand = static::convertScientificNotationToString($leftOperand);
69
        $rightOperand = static::convertScientificNotationToString($rightOperand);
70 161
71 161
        if (static::checkIsFloat($rightOperand)) {
72
            if (null === $scale) {
73 161
                $r = static::powFractional($leftOperand, $rightOperand);
74 8
            } else {
75
                $r = static::powFractional($leftOperand, $rightOperand, $scale);
76
            }
77 8
        } elseif (null === $scale) {
78
            $r = bcpow($leftOperand, $rightOperand);
79 153
        } else {
80 85
            $r = bcpow($leftOperand, $rightOperand, $scale);
81
        }
82 71
83
        return static::trimTrailingZeroes($r);
84
    }
85 161
86
    protected static function checkIsFloat(string $number): bool
87
    {
88 288
        return false !== strpos($number, '.');
89
    }
90 288
91
    protected static function powFractional(string $leftOperand, string $rightOperand, ?int $scale = null): string
92
    {
93 8
        // we need to increased scale to get correct results and avoid rounding error
94
        $currentScale = $scale ?? static::getScale();
95
        $increasedScale = $currentScale * 2;
96 8
97 8
        // add zero to trim scale
98
        return static::checkNumber(
99
            static::add(
100 8
                static::exp(static::mul($rightOperand, static::log($leftOperand), $increasedScale)),
101
                '0',
102
                $currentScale
103 38
            )
104
        );
105 38
    }
106
107
    public static function getScale(): int
108
    {
109 38
        if (PHP_VERSION_ID >= 70300) {
110
            /** @noinspection PhpStrictTypeCheckingInspection */
111 38
            /** @noinspection PhpParamsInspection */
112
            return bcscale();
0 ignored issues
show
Bug Best Practice introduced by
The expression return bcscale() returns the type boolean which is incompatible with the type-hinted return integer.
Loading history...
Bug introduced by
The call to bcscale() has too few arguments starting with scale. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

112
            return /** @scrutinizer ignore-call */ bcscale();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
113
        }
114 43
115
        $sqrt = static::sqrt('2');
116 43
117
        return strlen(substr($sqrt, strpos($sqrt, '.') + 1));
118 43
    }
119 39
120
    public static function sqrt(string $operand, ?int $scale = null): string
121 5
    {
122
        $operand = static::convertScientificNotationToString($operand);
123
124 43
        if (null === $scale) {
125
            $r = bcsqrt($operand);
126
        } else {
127 308
            $r = bcsqrt($operand, $scale);
128
        }
129 308
130 234
        return static::trimTrailingZeroes($r);
131
    }
132
133 308
    protected static function trimTrailingZeroes(string $number): string
134
    {
135
        if (!self::$trimTrailingZeroes) {
136 391
            return $number;
137
        }
138 391
139 391
        if (false !== strpos($number, '.')) {
140 28
            $number = rtrim($number, '0');
141
        }
142
143 388
        return rtrim($number, '.') ?: '0';
144
    }
145
146 181
    protected static function checkNumber(string $number): string
147
    {
148 181
        $number = str_replace('+', '', filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
149 181
        if ('-0' === $number || !is_numeric($number)) {
150
            return '0';
151 181
        }
152 51
153
        return $number;
154 130
    }
155
156
    public static function add(string $leftOperand, string $rightOperand, ?int $scale = null): string
157 181
    {
158
        $leftOperand = static::convertScientificNotationToString($leftOperand);
159
        $rightOperand = static::convertScientificNotationToString($rightOperand);
160 16
161
        if (null === $scale) {
162 16
            $r = bcadd($leftOperand, $rightOperand);
163 16
        } else {
164 16
            $r = bcadd($leftOperand, $rightOperand, $scale);
165 16
        }
166
167
        return static::trimTrailingZeroes($r);
168 16
    }
169
170
    public static function exp(string $arg): string
171 178
    {
172
        $scale = static::DEFAULT_SCALE;
173 178
        $result = '1';
174 178
        for ($i = 299; $i > 0; $i--) {
175
            $result = static::add(static::mul(static::div($result, (string)$i, $scale), $arg, $scale), '1', $scale);
176 178
        }
177 41
178
        return $result;
179 140
    }
180
181
    public static function mul(string $leftOperand, string $rightOperand, ?int $scale = null): string
182 178
    {
183
        $leftOperand = static::convertScientificNotationToString($leftOperand);
184
        $rightOperand = static::convertScientificNotationToString($rightOperand);
185 145
186
        if (null === $scale) {
187 145
            $r = bcmul($leftOperand, $rightOperand);
188 145
        } else {
189
            $r = bcmul($leftOperand, $rightOperand, $scale);
190 145
        }
191 1
192
        return static::trimTrailingZeroes($r);
193
    }
194 144
195 40
    public static function div(string $leftOperand, string $rightOperand, ?int $scale = null): string
196
    {
197 111
        $leftOperand = static::convertScientificNotationToString($leftOperand);
198
        $rightOperand = static::convertScientificNotationToString($rightOperand);
199
200 144
        if ('0' === static::trimTrailingZeroes($rightOperand)) {
201
            throw new InvalidArgumentException('Division by zero');
202
        }
203 13
204
        if (null === $scale) {
205 13
            $r = bcdiv($leftOperand, $rightOperand);
206 13
        } else {
207 1
            $r = bcdiv($leftOperand, $rightOperand, $scale);
208
        }
209 12
210 1
        return static::trimTrailingZeroes($r);
211
    }
212 11
213 11
    public static function log(string $arg): string
214 11
    {
215 11
        $arg = static::convertScientificNotationToString($arg);
216 11
        if ($arg === '0') {
217 11
            return '-INF';
218
        }
219 11
        if (static::COMPARE_RIGHT_GRATER === static::comp($arg, '0')) {
220 11
            return 'NAN';
221 11
        }
222 11
        $scale = static::DEFAULT_SCALE;
223
        $m = (string)log((float)$arg);
224 10
        $x = static::sub(static::div($arg, static::exp($m), $scale), '1', $scale);
225
        $res = '0';
226 11
        $pow = '1';
227 11
        $i = 1;
228
        do {
229 11
            $pow = static::mul($pow, $x, $scale);
230
            $sum = static::div($pow, (string)$i, $scale);
231
            if ($i % 2 === 1) {
232 65
                $res = static::add($res, $sum, $scale);
233
            } else {
234 65
                $res = static::sub($res, $sum, $scale);
235 65
            }
236
            $i++;
237 65
        } while (static::comp($sum, '0', $scale));
238 53
239
        return static::add($res, $m, $scale);
240
    }
241 23
242 23
    public static function comp(string $leftOperand, string $rightOperand, ?int $scale = null): int
243 23
    {
244 23
        $leftOperand = static::convertScientificNotationToString($leftOperand);
245
        $rightOperand = static::convertScientificNotationToString($rightOperand);
246
247
        if (null === $scale) {
248 82
            return bccomp($leftOperand, $rightOperand, max(strlen($leftOperand), strlen($rightOperand)));
249
        }
250 82
251 82
        return bccomp(
252
            $leftOperand,
253 82
            $rightOperand,
254 42
            $scale
255
        );
256 41
    }
257
258
    public static function sub(string $leftOperand, string $rightOperand, ?int $scale = null): string
259 82
    {
260
        $leftOperand = static::convertScientificNotationToString($leftOperand);
261
        $rightOperand = static::convertScientificNotationToString($rightOperand);
262 170
263
        if (null === $scale) {
264 170
            $r = bcsub($leftOperand, $rightOperand);
265
        } else {
266
            $r = bcsub($leftOperand, $rightOperand, $scale);
267 2
        }
268
269 2
        return static::trimTrailingZeroes($r);
270 2
    }
271
272 2
    public static function setTrimTrailingZeroes(bool $flag): void
273 2
    {
274
        self::$trimTrailingZeroes = $flag;
275 2
    }
276
277
    public static function max(...$ags): ?string
278 1
    {
279
        $max = null;
280 1
        foreach (static::parseArgs($ags) as $number) {
281 1
            $number = static::convertScientificNotationToString((string)$number);
282 1
            if (null === $max) {
283 1
                $max = $number;
284 1
            } elseif (static::comp($max, $number) === static::COMPARE_RIGHT_GRATER) {
0 ignored issues
show
Bug introduced by
$max of type void is incompatible with the type string expected by parameter $leftOperand of BCMathExtended\BC::comp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

284
            } elseif (static::comp(/** @scrutinizer ignore-type */ $max, $number) === static::COMPARE_RIGHT_GRATER) {
Loading history...
285 1
                $max = $number;
286 1
            }
287
        }
288
289
        return $max;
290 1
    }
291
292
    protected static function parseArgs(array $args): array
293 2
    {
294
        if (is_array($args[0])) {
295 2
            $args = $args[0];
296 2
        }
297
298
        return $args;
299 2
    }
300
301
    public static function min(...$ags): ?string
302 1
    {
303
        $min = null;
304 1
        foreach (static::parseArgs($ags) as $number) {
305 1
            $number = static::convertScientificNotationToString((string)$number);
306 1
            if (null === $min) {
307 1
                $min = $number;
308 1
            } elseif (static::comp($min, $number) === static::COMPARE_LEFT_GRATER) {
0 ignored issues
show
Bug introduced by
$min of type void is incompatible with the type string expected by parameter $leftOperand of BCMathExtended\BC::comp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

308
            } elseif (static::comp(/** @scrutinizer ignore-type */ $min, $number) === static::COMPARE_LEFT_GRATER) {
Loading history...
309 1
                $min = $number;
310 1
            }
311
        }
312
313
        return $min;
314 1
    }
315
316
    public static function powMod(
317 25
        string $leftOperand,
318
        string $rightOperand,
319 25
        string $modulus,
320 25
        ?int $scale = null
321
    ): string
322 25
    {
323
        $leftOperand = static::convertScientificNotationToString($leftOperand);
324 4
        $rightOperand = static::convertScientificNotationToString($rightOperand);
325 4
326 4
        // bcpowmod in 5.6 have don't calculate correct results if scale is empty
327
        if (null === $scale) {
328
            $r = static::mod(static::pow($leftOperand, $rightOperand), $modulus);
329 21
        } elseif (static::checkIsFloat($leftOperand) || static::checkIsFloat($rightOperand) || static::checkIsFloat(
330 21
                $modulus
331 25
            )) {
332
            // cant use bcpowmod here as it don't support floats
333
            $r = static::mod(static::pow($leftOperand, $rightOperand, $scale), $modulus, $scale);
334
        } else {
335 104
            $r = bcpowmod($leftOperand, $rightOperand, $modulus, $scale);
336
        }
337 104
338 104
        return static::trimTrailingZeroes($r);
0 ignored issues
show
Bug introduced by
It seems like $r can also be of type null; however, parameter $number of BCMathExtended\BC::trimTrailingZeroes() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

338
        return static::trimTrailingZeroes(/** @scrutinizer ignore-type */ $r);
Loading history...
339 44
    }
340 44
341 9
    public static function mod(string $leftOperand, string $modulus, ?int $scale = null): string
342
    {
343 44
        $leftOperand = static::convertScientificNotationToString($leftOperand);
344
345
        // 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
346 104
347
        // bcmod in php 5.6< don't support scale and floats
348
        // let use this $x - floor($x/$y) * $y;
349 75
        if (null === $scale) {
350
            $r = static::sub(
351 75
                $leftOperand,
352
                static::mul(static::floor(static::div($leftOperand, $modulus)), $modulus)
353
            );
354 24
        } else {
355
            $r = static::sub(
356 24
                $leftOperand,
357 24
                static::mul(static::floor(static::div($leftOperand, $modulus, $scale)), $modulus, $scale),
358
                $scale
359 24
            );
360
        }
361 4
362 4
        return static::trimTrailingZeroes($r);
363 4
    }
364
365
    public static function floor(string $number): string
366 20
    {
367 20
        $number = static::convertScientificNotationToString($number);
368 24
        if (static::checkIsFloat($number) && static::checkIsFloatCleanZeros($number)) {
369
            $result = 0;
370
            if (static::isNegative($number)) {
371
                --$result;
372 48
            }
373
            $number = static::add($number, (string)$result, 0);
374 48
        }
375 48
376 29
        return static::checkNumber($number);
377 29
    }
378 6
379
    protected static function checkIsFloatCleanZeros(string &$number): bool
380 29
    {
381
        return false !== strpos($number = static::trimTrailingZeroes($number), '.');
382
    }
383 48
384
    protected static function isNegative(string $number): bool
385
    {
386 10
        return 0 === strncmp('-', $number, 1);
387
    }
388
389
    public static function fact(string $arg): string
390
    {
391
        $arg = static::convertScientificNotationToString($arg);
392 10
393 10
        if (static::checkIsFloat($arg)) {
394
            throw new InvalidArgumentException('Number has to be an integer');
395
        }
396 10
        if (static::isNegative($arg)) {
397
            throw new InvalidArgumentException('Number has to be greater than or equal to 0');
398 10
        }
399 10
400
        $return = '1';
401
        for ($i = 2; $i <= $arg; ++$i) {
402 4
            $return = static::mul($return, (string)$i);
403
        }
404 6
405
        return $return;
406
    }
407 10
408
    public static function hexdec(string $hex): string
409
    {
410 54
        $remainingDigits = substr($hex, 0, -1);
411
        $lastDigitToDecimal = (string)hexdec(substr($hex, -1));
412 54
413
        if ('' === $remainingDigits) {
414
            return $lastDigitToDecimal;
415
        }
416
417
        return static::add(static::mul('16', static::hexdec($remainingDigits)), $lastDigitToDecimal, 0);
418 54
    }
419 39
420 39
    public static function dechex(string $decimal): string
421 39
    {
422
        $quotient = static::div($decimal, '16', 0);
423
        $remainderToHex = dechex((int)static::mod($decimal, '16'));
424 15
425 15
        if (static::comp($quotient, '0') === static::COMPARE_EQUAL) {
426 15
            return $remainderToHex;
427 15
        }
428
429
        return static::dechex($quotient) . $remainderToHex;
430
    }
431 54
432
    public static function bitAnd(
433
        string $leftOperand,
434 8
        string $rightOperand
435
    ): string {
436 8
        return static::bitOperatorHelper($leftOperand, $rightOperand, static::BIT_OPERATOR_AND);
437
    }
438 8
439 1
    protected static function bitOperatorHelper(string $leftOperand, string $rightOperand, string $operator): string
440
    {
441 7
        $leftOperand = static::convertScientificNotationToString($leftOperand);
442 1
        $rightOperand = static::convertScientificNotationToString($rightOperand);
443
444
        if (static::checkIsFloat($leftOperand)) {
445 6
            throw new InvalidArgumentException('Left operator has to be an integer');
446 6
        }
447 5
        if (static::checkIsFloat($rightOperand)) {
448
            throw new InvalidArgumentException('Right operator has to be an integer');
449
        }
450 6
451
        $leftOperandNegative = static::isNegative($leftOperand);
452
        $rightOperandNegative = static::isNegative($rightOperand);
453 5
454
        $leftOperand = static::dec2bin(static::abs($leftOperand));
455 5
        $rightOperand = static::dec2bin(static::abs($rightOperand));
456 5
457
        $maxLength = max(strlen($leftOperand), strlen($rightOperand));
458 5
459 5
        $leftOperand = static::alignBinLength($leftOperand, $maxLength);
460
        $rightOperand = static::alignBinLength($rightOperand, $maxLength);
461
462 5
        if ($leftOperandNegative) {
463
            $leftOperand = static::recalculateNegative($leftOperand);
464
        }
465 6
        if ($rightOperandNegative) {
466
            $rightOperand = static::recalculateNegative($rightOperand);
467 6
        }
468 6
469
        $isNegative = false;
470 6
        $result = '';
471 6
        if (static::BIT_OPERATOR_AND === $operator) {
472
            $result = $leftOperand & $rightOperand;
473
            $isNegative = ($leftOperandNegative and $rightOperandNegative);
474 6
        } elseif (static::BIT_OPERATOR_OR === $operator) {
475
            $result = $leftOperand | $rightOperand;
476
            $isNegative = ($leftOperandNegative or $rightOperandNegative);
477 12
        } elseif (static::BIT_OPERATOR_XOR === $operator) {
478
            $result = $leftOperand ^ $rightOperand;
479
            $isNegative = ($leftOperandNegative xor $rightOperandNegative);
480
        }
481 12
482
        if ($isNegative) {
483
            $result = static::recalculateNegative($result);
484 36
        }
485
486 36
        $result = static::bin2dec($result);
487 36
488
        return $isNegative ? '-' . $result : $result;
489 36
    }
490 3
491
    public static function dec2bin(string $number, int $base = self::MAX_BASE): string
492 33
    {
493 3
        return static::decBaseHelper(
494
            $base,
495
            static function (int $base) use ($number) {
496 30
                $value = '';
497 30
                if ('0' === $number) {
498
                    return chr((int)$number);
499 30
                }
500 30
501
                while (BC::comp($number, '0') !== BC::COMPARE_EQUAL) {
502 30
                    $rest = BC::mod($number, (string)$base);
503
                    $number = BC::div($number, (string)$base);
504 30
                    $value = chr((int)$rest) . $value;
505 30
                }
506
507 30
                return $value;
508 7
            }
509
        );
510 30
    }
511 7
512
    protected static function decBaseHelper(int $base, Closure $closure): string
513
    {
514 30
        if ($base < 2 || $base > static::MAX_BASE) {
515 30
            throw new InvalidArgumentException('Invalid Base: ' . $base);
516 30
        }
517 10
        $orgScale = static::getScale();
518 10
        static::setScale(0);
519 20
520 12
        $value = $closure($base);
521 12
522 8
        static::setScale($orgScale);
523 8
524 8
        return $value;
525
    }
526
527 30
    public static function setScale(int $scale): void
528 8
    {
529
        bcscale($scale);
530
    }
531 30
532
    public static function abs(string $number): string
533 30
    {
534
        $number = static::convertScientificNotationToString($number);
535
536 36
        if (static::isNegative($number)) {
537
            $number = (string)substr($number, 1);
538 36
        }
539
540 35
        return static::checkNumber($number);
541 35
    }
542 30
543
    protected static function alignBinLength(string $string, int $length): string
544
    {
545 33
        return str_pad($string, $length, static::dec2bin('0'), STR_PAD_LEFT);
546 33
    }
547 33
548 33
    protected static function recalculateNegative(string $number): string
549
    {
550
        $xor = str_repeat(static::dec2bin((string)(static::MAX_BASE - 1)), strlen($number));
551 33
        $number ^= $xor;
552 36
        for ($i = strlen($number) - 1; $i >= 0; $i--) {
553
            $byte = ord($number[$i]);
554
            if (++$byte !== static::MAX_BASE) {
555
                $number[$i] = chr($byte);
556 37
                break;
557
            }
558 37
        }
559 2
560
        return $number;
561 35
    }
562 35
563
    public static function bin2dec(string $binary, int $base = self::MAX_BASE): string
564 35
    {
565
        return static::decBaseHelper(
566 35
            $base,
567
            static function (int $base) use ($binary) {
568 35
                $size = strlen($binary);
569
                $return = '0';
570
                for ($i = 0; $i < $size; ++$i) {
571 393
                    $element = ord($binary[$i]);
572
                    $power = BC::pow((string)$base, (string)($size - $i - 1));
573 393
                    $return = BC::add($return, BC::mul((string)$element, $power));
574 393
                }
575
576 45
                return $return;
577
            }
578 45
        );
579
    }
580 45
581 19
    public static function bitOr(string $leftOperand, string $rightOperand): string
582
    {
583
        return static::bitOperatorHelper($leftOperand, $rightOperand, static::BIT_OPERATOR_OR);
584 45
    }
585
586
    public static function bitXor(string $leftOperand, string $rightOperand): string
587 30
    {
588
        return static::bitOperatorHelper($leftOperand, $rightOperand, static::BIT_OPERATOR_XOR);
589 30
    }
590
591
    public static function roundHalfEven(string $number, int $precision = 0): string
592 11
    {
593
        $number = static::convertScientificNotationToString($number);
594 11
        if (!static::checkIsFloat($number)) {
595 11
            return static::checkNumber($number);
596 11
        }
597 11
598 11
        $precessionPos = strpos($number, '.') + $precision + 1;
599 11
        if (strlen($number) <= $precessionPos) {
600 11
            return static::round($number, $precision);
601
        }
602
603
        if ($number[$precessionPos] !== '5') {
604 11
            return static::round($number, $precision);
605
        }
606
607 36
        $isPrevEven = $number[$precessionPos - 1] === '.'
608
            ? (int)$number[$precessionPos - 2] % 2 === 0
609 36
            : (int)$number[$precessionPos - 1] % 2 === 0;
610
        $isNegative = static::isNegative($number);
611 35
612 35
        if ($isPrevEven === $isNegative) {
613 35
            return static::roundUp($number, $precision);
614 35
        }
615 35
616 35
        return static::roundDown($number, $precision);
617
    }
618
619 35
    public static function round(string $number, int $precision = 0): string
620 36
    {
621
        $number = static::convertScientificNotationToString($number);
622
        if (static::checkIsFloat($number)) {
623
            if (static::isNegative($number)) {
624 14
                return static::sub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
625
            }
626 14
627
            return static::add($number, '0.' . str_repeat('0', $precision) . '5', $precision);
628
        }
629 10
630
        return static::checkNumber($number);
631 10
    }
632
633
    public static function roundUp(string $number, int $precision = 0): string
634 24
    {
635
        $number = static::convertScientificNotationToString($number);
636 24
        $multiply = static::pow('10', (string)abs($precision));
637 24
638
        return $precision < 0
639
            ?
640
            static::mul(
641 24
                static::ceil(static::div($number, $multiply, static::getDecimalsLengthFromNumber($number))),
642 24
                $multiply,
643 1
                $precision
644
            )
645
            :
646 23
            static::div(
647 8
                static::ceil(static::mul($number, $multiply, static::getDecimalsLengthFromNumber($number))),
648
                $multiply,
649
                $precision
650 15
            );
651 4
    }
652 15
653
    public static function ceil(string $number): string
654 15
    {
655
        $number = static::convertScientificNotationToString($number);
656 15
        if (static::checkIsFloat($number) && static::checkIsFloatCleanZeros($number)) {
657 7
            $result = 1;
658
            if (static::isNegative($number)) {
659
                --$result;
660 8
            }
661
            $number = static::add($number, (string)$result, 0);
662
        }
663
664
        return static::checkNumber($number);
665
    }
666
667
    public static function roundDown(string $number, int $precision = 0): string
668
    {
669
        $number = static::convertScientificNotationToString($number);
670
        $multiply = static::pow('10', (string)abs($precision));
671
672
        return $precision < 0
673
            ?
674
            static::mul(
675
                static::floor(static::div($number, $multiply, static::getDecimalsLengthFromNumber($number))),
676
                $multiply,
677
                $precision
678
            )
679
            :
680
            static::div(
681
                static::floor(static::mul($number, $multiply, static::getDecimalsLengthFromNumber($number))),
682
                $multiply,
683
                $precision
684
            );
685
    }
686
}
687