Completed
Push — master ( 834c41...4379ac )
by kacper
02:11
created

BC   F

Complexity

Total Complexity 113

Size/Duplication

Total Lines 621
Duplicated Lines 0 %

Test Coverage

Coverage 99.07%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 297
c 2
b 0
f 1
dl 0
loc 621
ccs 318
cts 321
cp 0.9907
rs 2
wmc 113

43 Methods

Rating   Name   Duplication   Size   Complexity  
A convertScientificNotationToString() 0 17 4
A getDecimalsLengthFromNumber() 0 8 2
A pow() 0 18 4
A round() 0 12 3
A checkIsFloat() 0 3 1
A floor() 0 12 4
A checkIsFloatCleanZeros() 0 3 1
A dechex() 0 10 2
A add() 0 12 2
A rand() 0 9 1
A comp() 0 13 2
A bin2dec() 0 13 2
A max() 0 13 4
A sqrt() 0 11 2
A powMod() 0 22 5
C bitOperatorHelper() 0 50 12
A alignBinLength() 0 3 1
A dec2bin() 0 16 3
A exp() 0 9 2
A isNegative() 0 3 1
A parseArgs() 0 7 2
A checkNumber() 0 8 3
A abs() 0 9 2
A min() 0 13 4
A powFractional() 0 8 1
A roundUp() 0 15 2
A getScale() 0 9 2
A log() 0 27 5
A trimTrailingZeroes() 0 7 3
A hexdec() 0 10 2
A roundDown() 0 15 2
A decBaseHelper() 0 13 3
A setScale() 0 3 1
A mod() 0 22 2
A mul() 0 12 2
A fact() 0 17 4
A ceil() 0 12 4
A bitXor() 0 3 1
A sub() 0 12 2
A div() 0 16 3
A bitAnd() 0 5 1
A bitOr() 0 3 1
A recalculateNegative() 0 13 3

How to fix   Complexity   

Complex Class

Complex classes like BC often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BC, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
4
namespace BCMathExtended;
5
6
use Closure;
7
use InvalidArgumentException;
8
use function dechex;
9
use function hexdec;
10
11
class BC
12
{
13
    public const COMPARE_EQUAL = 0;
14
    public const COMPARE_LEFT_GRATER = 1;
15
    public const COMPARE_RIGHT_GRATER = -1;
16
17
    protected const DEFAULT_SCALE = 100;
18
19
    protected const MAX_BASE = 256;
20
21
    protected const BIT_OPERATOR_AND = 'and';
22
    protected const BIT_OPERATOR_OR = 'or';
23
    protected const BIT_OPERATOR_XOR = 'xor';
24
25 47
    public static function round(string $number, int $precision = 0): string
26
    {
27 47
        $number = self::convertScientificNotationToString($number);
28 47
        if (self::checkIsFloat($number)) {
29 36
            if (self::isNegative($number)) {
30 4
                return self::sub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
31
            }
32
33 32
            return self::add($number, '0.' . str_repeat('0', $precision) . '5', $precision);
34
        }
35
36 11
        return self::checkNumber($number);
37
    }
38
39 367
    public static function convertScientificNotationToString(string $number): string
40
    {
41
        // check if number is in scientific notation, first use stripos as is faster then preg_match
42 367
        if (false !== stripos($number, 'E') && preg_match('/(-?(\d+\.)?\d+)E([+-]?)(\d+)/i', $number, $regs)) {
43
            // calculate final scale of number
44 60
            $scale = $regs[4] + self::getDecimalsLengthFromNumber($regs[1]);
45 60
            $pow = self::pow('10', $regs[4], $scale);
46 60
            if ('-' === $regs[3]) {
47 25
                $number = self::div($regs[1], $pow, $scale);
48
            } else {
49 35
                $number = self::mul($pow, $regs[1], $scale);
50
            }
51
            // remove unnecessary 0 and dot from 0.000 is a 0
52 60
            $number = self::trimTrailingZeroes($number);
53
        }
54
55 367
        return self::checkNumber($number);
56
    }
57
58 92
    public static function getDecimalsLengthFromNumber(string $number): int
59
    {
60 92
        $check = explode('.', $number);
61 92
        if (!empty($check[1])) {
62 62
            return strlen($check[1]);
63
        }
64
65 30
        return 0;
66
    }
67
68 146
    public static function pow(string $leftOperand, string $rightOperand, ?int $scale = null): string
69
    {
70 146
        $leftOperand = self::convertScientificNotationToString($leftOperand);
71 146
        $rightOperand = self::convertScientificNotationToString($rightOperand);
72
73 146
        if (self::checkIsFloat($rightOperand)) {
74 8
            if (null === $scale) {
75
                $r = self::powFractional($leftOperand, $rightOperand);
76
            } else {
77 8
                $r = self::powFractional($leftOperand, $rightOperand, $scale);
78
            }
79 138
        } else if (null === $scale) {
80 70
            $r = bcpow($leftOperand, $rightOperand);
81
        } else {
82 71
            $r = bcpow($leftOperand, $rightOperand, $scale);
83
        }
84
85 146
        return self::trimTrailingZeroes($r);
86
    }
87
88 264
    protected static function checkIsFloat(string $number): bool
89
    {
90 264
        return false !== strpos($number, '.');
91
    }
92
93 8
    protected static function powFractional(string $leftOperand, string $rightOperand, ?int $scale = null): string
94
    {
95
        // we need to increased scale to get correct results and avoid rounding error
96 8
        $currentScale = $scale ?? self::getScale();
97 8
        $increasedScale = $currentScale * 2;
98
99
        // add zero to trim scale
100 8
        return self::checkNumber(self::add(self::exp(self::mul($rightOperand, self::log($leftOperand), $increasedScale)), '0', $currentScale));
101
    }
102
103 38
    public static function getScale(): int
104
    {
105 38
        if (PHP_VERSION_ID >= 70300) {
106
            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

106
            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...
107
        }
108
109 38
        $sqrt = self::sqrt('2');
110
111 38
        return strlen(substr($sqrt, strpos($sqrt, '.') + 1));
112
    }
113
114 43
    public static function sqrt(string $operand, ?int $scale = null): string
115
    {
116 43
        $operand = self::convertScientificNotationToString($operand);
117
118 43
        if (null === $scale) {
119 39
            $r = bcsqrt($operand);
120
        } else {
121 5
            $r = bcsqrt($operand, $scale);
122
        }
123
124 43
        return self::trimTrailingZeroes($r);
125
    }
126
127 284
    protected static function trimTrailingZeroes(string $number): string
128
    {
129 284
        if (false !== strpos($number, '.')) {
130 218
            $number = rtrim($number, '0');
131
        }
132
133 284
        return rtrim($number, '.') ?: '0';
134
    }
135
136 367
    protected static function checkNumber(string $number): string
137
    {
138 367
        $number = str_replace('+', '', filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
139 367
        if ('-0' === $number || !is_numeric($number)) {
140 27
            return '0';
141
        }
142
143 364
        return $number;
144
    }
145
146 166
    public static function mul(string $leftOperand, string $rightOperand, ?int $scale = null): string
147
    {
148 166
        $leftOperand = self::convertScientificNotationToString($leftOperand);
149 166
        $rightOperand = self::convertScientificNotationToString($rightOperand);
150
151 166
        if (null === $scale) {
152 51
            $r = bcmul($leftOperand, $rightOperand);
153
        } else {
154 115
            $r = bcmul($leftOperand, $rightOperand, $scale);
155
        }
156
157 166
        return self::trimTrailingZeroes($r);
158
    }
159
160 16
    public static function exp(string $arg): string
161
    {
162 16
        $scale = self::DEFAULT_SCALE;
163 16
        $result = '1';
164 16
        for ($i = 299; $i > 0; $i--) {
165 16
            $result = self::add(self::mul(self::div($result, (string)$i, $scale), $arg, $scale), '1', $scale);
166
        }
167
168 16
        return $result;
169
    }
170
171 159
    public static function add(string $leftOperand, string $rightOperand, ?int $scale = null): string
172
    {
173 159
        $leftOperand = self::convertScientificNotationToString($leftOperand);
174 159
        $rightOperand = self::convertScientificNotationToString($rightOperand);
175
176 159
        if (null === $scale) {
177 41
            $r = bcadd($leftOperand, $rightOperand);
178
        } else {
179 121
            $r = bcadd($leftOperand, $rightOperand, $scale);
180
        }
181
182 159
        return self::trimTrailingZeroes($r);
183
    }
184
185 130
    public static function div(string $leftOperand, string $rightOperand, ?int $scale = null): string
186
    {
187 130
        $leftOperand = self::convertScientificNotationToString($leftOperand);
188 130
        $rightOperand = self::convertScientificNotationToString($rightOperand);
189
190 130
        if ('0' === self::trimTrailingZeroes($rightOperand)) {
191 1
            throw new InvalidArgumentException('Division by zero');
192
        }
193
194 129
        if (null === $scale) {
195 40
            $r = bcdiv($leftOperand, $rightOperand);
196
        } else {
197 96
            $r = bcdiv($leftOperand, $rightOperand, $scale);
198
        }
199
200 129
        return self::trimTrailingZeroes($r);
201
    }
202
203 13
    public static function log(string $arg): string
204
    {
205 13
        $arg = self::convertScientificNotationToString($arg);
206 13
        if ($arg === '0') {
207 1
            return '-INF';
208
        }
209 12
        if (self::COMPARE_RIGHT_GRATER === self::comp($arg, '0')) {
210 1
            return 'NAN';
211
        }
212 11
        $scale = self::DEFAULT_SCALE;
213 11
        $m = (string)log((float)$arg);
214 11
        $x = self::sub(self::div($arg, self::exp($m), $scale), '1', $scale);
215 11
        $res = '0';
216 11
        $pow = '1';
217 11
        $i = 1;
218
        do {
219 11
            $pow = self::mul($pow, $x, $scale);
220 11
            $sum = self::div($pow, (string)$i, $scale);
221 11
            if ($i % 2 === 1) {
222 11
                $res = self::add($res, $sum, $scale);
223
            } else {
224 10
                $res = self::sub($res, $sum, $scale);
225
            }
226 11
            $i++;
227 11
        } while (self::comp($sum, '0', $scale));
228
229 11
        return self::add($res, $m, $scale);
230
    }
231
232 65
    public static function comp(string $leftOperand, string $rightOperand, ?int $scale = null): int
233
    {
234 65
        $leftOperand = self::convertScientificNotationToString($leftOperand);
235 65
        $rightOperand = self::convertScientificNotationToString($rightOperand);
236
237 65
        if (null === $scale) {
238 53
            return bccomp($leftOperand, $rightOperand, max(strlen($leftOperand), strlen($rightOperand)));
239
        }
240
241 23
        return bccomp(
242 23
            $leftOperand,
243 23
            $rightOperand,
244 23
            $scale
245
        );
246
    }
247
248 77
    public static function sub(string $leftOperand, string $rightOperand, ?int $scale = null): string
249
    {
250 77
        $leftOperand = self::convertScientificNotationToString($leftOperand);
251 77
        $rightOperand = self::convertScientificNotationToString($rightOperand);
252
253 77
        if (null === $scale) {
254 42
            $r = bcsub($leftOperand, $rightOperand);
255
        } else {
256 36
            $r = bcsub($leftOperand, $rightOperand, $scale);
257
        }
258
259 77
        return self::trimTrailingZeroes($r);
260
    }
261
262 146
    protected static function isNegative(string $number): bool
263
    {
264 146
        return 0 === strncmp('-', $number, 1);
265
    }
266
267 2
    public static function rand(string $min, string $max): string
268
    {
269 2
        $max = self::convertScientificNotationToString($max);
270 2
        $min = self::convertScientificNotationToString($min);
271
272 2
        $difference = self::add(self::sub($max, $min), '1');
273 2
        $randPercent = self::div((string)mt_rand(), (string)mt_getrandmax(), 8);
274
275 2
        return self::add($min, self::mul($difference, $randPercent, 8), 0);
276
    }
277
278 1
    public static function max(...$ags): ?string
279
    {
280 1
        $max = null;
281 1
        foreach (self::parseArgs($ags) as $number) {
282 1
            $number = self::convertScientificNotationToString((string)$number);
283 1
            if (null === $max) {
284 1
                $max = $number;
285 1
            } elseif (self::comp((string)$max, $number) === self::COMPARE_RIGHT_GRATER) {
286 1
                $max = $number;
287
            }
288
        }
289
290 1
        return $max;
291
    }
292
293 2
    protected static function parseArgs(array $args): array
294
    {
295 2
        if (is_array($args[0])) {
296 2
            $args = $args[0];
297
        }
298
299 2
        return $args;
300
    }
301
302 1
    public static function min(...$ags): ?string
303
    {
304 1
        $min = null;
305 1
        foreach (self::parseArgs($ags) as $number) {
306 1
            $number = self::convertScientificNotationToString((string)$number);
307 1
            if (null === $min) {
308 1
                $min = $number;
309 1
            } elseif (self::comp((string)$min, $number) === self::COMPARE_LEFT_GRATER) {
310 1
                $min = $number;
311
            }
312
        }
313
314 1
        return $min;
315
    }
316
317 17
    public static function roundDown(string $number, int $precision = 0): string
318
    {
319 17
        $number = self::convertScientificNotationToString($number);
320 17
        $multiply = self::pow('10', (string)abs($precision));
321
322 17
        return $precision < 0
323
            ?
324 4
            self::mul(
325 4
                self::floor(self::div($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
326 4
                $precision
327
            )
328
            :
329 13
            self::div(
330 13
                self::floor(self::mul($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
331 17
                $precision
332
            );
333
    }
334
335 96
    public static function floor(string $number): string
336
    {
337 96
        $number = self::convertScientificNotationToString($number);
338 96
        if (self::checkIsFloat($number) && self::checkIsFloatCleanZeros($number)) {
339 36
            $result = 0;
340 36
            if (self::isNegative($number)) {
341 7
                --$result;
342
            }
343 36
            $number = self::add($number, (string)$result, 0);
344
        }
345
346 96
        return self::checkNumber($number);
347
    }
348
349 60
    protected static function checkIsFloatCleanZeros(string &$number): bool
350
    {
351 60
        return false !== strpos($number = self::trimTrailingZeroes($number), '.');
352
    }
353
354 17
    public static function roundUp(string $number, int $precision = 0): string
355
    {
356 17
        $number = self::convertScientificNotationToString($number);
357 17
        $multiply = self::pow('10', (string)abs($precision));
358
359 17
        return $precision < 0
360
            ?
361 4
            self::mul(
362 4
                self::ceil(self::div($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
363 4
                $precision
364
            )
365
            :
366 13
            self::div(
367 13
                self::ceil(self::mul($number, $multiply, self::getDecimalsLengthFromNumber($number))), $multiply,
368 17
                $precision
369
            );
370
    }
371
372 41
    public static function ceil(string $number): string
373
    {
374 41
        $number = self::convertScientificNotationToString($number);
375 41
        if (self::checkIsFloat($number) && self::checkIsFloatCleanZeros($number)) {
376 22
            $result = 1;
377 22
            if (self::isNegative($number)) {
378 5
                --$result;
379
            }
380 22
            $number = self::add($number, (string)$result, 0);
381
        }
382
383 41
        return self::checkNumber($number);
384
    }
385
386 10
    public static function powMod(
387
        string $leftOperand,
388
        string $rightOperand,
389
        string $modulus,
390
        ?int $scale = null
391
    ): string {
392 10
        $leftOperand = self::convertScientificNotationToString($leftOperand);
393 10
        $rightOperand = self::convertScientificNotationToString($rightOperand);
394
395
        // bcpowmod in 5.6 have don't calculate correct results if scale is empty
396 10
        if (null === $scale) {
397
            $r = self::mod(self::pow($leftOperand, $rightOperand), $modulus);
398 10
        } else if (self::checkIsFloat($leftOperand) || self::checkIsFloat($rightOperand) || self::checkIsFloat(
399 10
                $modulus
400
            )) {
401
            // cant use bcpowmod here as it don't support floats
402 4
            $r = self::mod(self::pow($leftOperand, $rightOperand, $scale), $modulus, $scale);
403
        } else {
404 6
            $r = bcpowmod($leftOperand, $rightOperand, $modulus, $scale);
405
        }
406
407 10
        return self::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

407
        return self::trimTrailingZeroes(/** @scrutinizer ignore-type */ $r);
Loading history...
408
    }
409
410 54
    public static function mod(string $leftOperand, string $modulus, ?int $scale = null): string
411
    {
412 54
        $leftOperand = self::convertScientificNotationToString($leftOperand);
413
414
        // 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
415
416
        // bcmod in php 5.6< don't support scale and floats
417
        // let use this $x - floor($x/$y) * $y;
418 54
        if (null === $scale) {
419 39
            $r = self::sub(
420 39
                $leftOperand,
421 39
                self::mul(self::floor(self::div($leftOperand, $modulus)), $modulus)
422
            );
423
        } else {
424 15
            $r = self::sub(
425 15
                $leftOperand,
426 15
                self::mul(self::floor(self::div($leftOperand, $modulus, $scale)), $modulus, $scale),
427 15
                $scale
428
            );
429
        }
430
431 54
        return self::trimTrailingZeroes($r);
432
    }
433
434 8
    public static function fact(string $arg): string
435
    {
436 8
        $arg = self::convertScientificNotationToString($arg);
437
438 8
        if (self::checkIsFloat($arg)) {
439 1
            throw new InvalidArgumentException('Number has to be an integer');
440
        }
441 7
        if (self::isNegative($arg)) {
442 1
            throw new InvalidArgumentException('Number has to be greater than or equal to 0');
443
        }
444
445 6
        $return = '1';
446 6
        for ($i = 2; $i <= $arg; ++$i) {
447 5
            $return = self::mul($return, (string)$i);
448
        }
449
450 6
        return $return;
451
    }
452
453 5
    public static function hexdec(string $hex): string
454
    {
455 5
        $remainingDigits = substr($hex, 0, -1);
456 5
        $lastDigitToDecimal = (string)hexdec(substr($hex, -1));
457
458 5
        if ('' === $remainingDigits) {
459 5
            return $lastDigitToDecimal;
460
        }
461
462 5
        return self::add(self::mul('16', self::hexdec($remainingDigits)), $lastDigitToDecimal, 0);
463
    }
464
465 6
    public static function dechex(string $decimal): string
466
    {
467 6
        $quotient = self::div($decimal, '16', 0);
468 6
        $remainderToHex = dechex((int)self::mod($decimal, '16'));
469
470 6
        if (self::comp($quotient, '0') === self::COMPARE_EQUAL) {
471 6
            return $remainderToHex;
472
        }
473
474 6
        return self::dechex($quotient) . $remainderToHex;
475
    }
476
477 12
    public static function bitAnd(
478
        string $leftOperand,
479
        string $rightOperand
480
    ): string {
481 12
        return self::bitOperatorHelper($leftOperand, $rightOperand, self::BIT_OPERATOR_AND);
482
    }
483
484 36
    protected static function bitOperatorHelper(string $leftOperand, string $rightOperand, string $operator): string
485
    {
486 36
        $leftOperand = self::convertScientificNotationToString($leftOperand);
487 36
        $rightOperand = self::convertScientificNotationToString($rightOperand);
488
489 36
        if (self::checkIsFloat($leftOperand)) {
490 3
            throw new InvalidArgumentException('Left operator has to be an integer');
491
        }
492 33
        if (self::checkIsFloat($rightOperand)) {
493 3
            throw new InvalidArgumentException('Right operator has to be an integer');
494
        }
495
496 30
        $leftOperandNegative = self::isNegative($leftOperand);
497 30
        $rightOperandNegative = self::isNegative($rightOperand);
498
499 30
        $leftOperand = self::dec2bin(self::abs($leftOperand));
500 30
        $rightOperand = self::dec2bin(self::abs($rightOperand));
501
502 30
        $maxLength = max(strlen($leftOperand), strlen($rightOperand));
503
504 30
        $leftOperand = self::alignBinLength($leftOperand, $maxLength);
505 30
        $rightOperand = self::alignBinLength($rightOperand, $maxLength);
506
507 30
        if ($leftOperandNegative) {
508 7
            $leftOperand = self::recalculateNegative($leftOperand);
509
        }
510 30
        if ($rightOperandNegative) {
511 7
            $rightOperand = self::recalculateNegative($rightOperand);
512
        }
513
514 30
        $isNegative = false;
515 30
        $result = '';
516 30
        if (self::BIT_OPERATOR_AND === $operator) {
517 10
            $result = $leftOperand & $rightOperand;
518 10
            $isNegative = ($leftOperandNegative and $rightOperandNegative);
519 20
        } elseif (self::BIT_OPERATOR_OR === $operator) {
520 12
            $result = $leftOperand | $rightOperand;
521 12
            $isNegative = ($leftOperandNegative or $rightOperandNegative);
522 8
        } elseif (self::BIT_OPERATOR_XOR === $operator) {
523 8
            $result = $leftOperand ^ $rightOperand;
524 8
            $isNegative = ($leftOperandNegative xor $rightOperandNegative);
525
        }
526
527 30
        if ($isNegative) {
528 8
            $result = self::recalculateNegative($result);
529
        }
530
531 30
        $result = self::bin2dec($result);
532
533 30
        return $isNegative ? '-' . $result : $result;
534
    }
535
536 36
    public static function dec2bin(string $number, int $base = self::MAX_BASE): string
537
    {
538 36
        return self::decBaseHelper(
539
            $base, static function (int $base) use ($number) {
540 35
            $value = '';
541 35
            if ('0' === $number) {
542 30
                return chr((int)$number);
543
            }
544
545 33
            while (BC::comp($number, '0') !== BC::COMPARE_EQUAL) {
546 33
                $rest = BC::mod($number, (string)$base);
547 33
                $number = BC::div($number, (string)$base);
548 33
                $value = chr((int)$rest) . $value;
549
            }
550
551 33
            return $value;
552 36
        }
553
        );
554
    }
555
556 37
    protected static function decBaseHelper(int $base, Closure $closure): string
557
    {
558 37
        if ($base < 2 || $base > self::MAX_BASE) {
559 2
            throw new InvalidArgumentException('Invalid Base: ' . $base);
560
        }
561 35
        $orgScale = self::getScale();
562 35
        self::setScale(0);
563
564 35
        $value = $closure($base);
565
566 35
        self::setScale($orgScale);
567
568 35
        return $value;
569
    }
570
571 369
    public static function setScale(int $scale): void
572
    {
573 369
        bcscale($scale);
574 369
    }
575
576 45
    public static function abs(string $number): string
577
    {
578 45
        $number = self::convertScientificNotationToString($number);
579
580 45
        if (self::isNegative($number)) {
581 19
            $number = (string)substr($number, 1);
582
        }
583
584 45
        return self::checkNumber($number);
585
    }
586
587 30
    protected static function alignBinLength(string $string, int $length): string
588
    {
589 30
        return str_pad($string, $length, self::dec2bin('0'), STR_PAD_LEFT);
590
    }
591
592 11
    protected static function recalculateNegative(string $number): string
593
    {
594 11
        $xor = str_repeat(self::dec2bin((string)(self::MAX_BASE - 1)), strlen($number));
595 11
        $number ^= $xor;
596 11
        for ($i = strlen($number) - 1; $i >= 0; $i--) {
597 11
            $byte = ord($number[$i]);
598 11
            if (++$byte !== self::MAX_BASE) {
599 11
                $number[$i] = chr($byte);
600 11
                break;
601
            }
602
        }
603
604 11
        return $number;
605
    }
606
607 36
    public static function bin2dec(string $binary, int $base = self::MAX_BASE): string
608
    {
609 36
        return self::decBaseHelper(
610
            $base, static function (int $base) use ($binary) {
611 35
            $size = strlen($binary);
612 35
            $return = '0';
613 35
            for ($i = 0; $i < $size; ++$i) {
614 35
                $element = ord($binary[$i]);
615 35
                $power = BC::pow((string)$base, (string)($size - $i - 1));
616 35
                $return = BC::add($return, BC::mul((string)$element, $power));
617
            }
618
619 35
            return $return;
620 36
        }
621
        );
622
    }
623
624 14
    public static function bitOr(string $leftOperand, string $rightOperand): string
625
    {
626 14
        return self::bitOperatorHelper($leftOperand, $rightOperand, self::BIT_OPERATOR_OR);
627
    }
628
629 10
    public static function bitXor(string $leftOperand, string $rightOperand): string
630
    {
631 10
        return self::bitOperatorHelper($leftOperand, $rightOperand, self::BIT_OPERATOR_XOR);
632
    }
633
}
634