Passed
Push — master ( d07e18...834c41 )
by kacper
01:45
created

BC::parseArgs()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
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 366
    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 366
        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 366
        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 145
    public static function pow(string $leftOperand, string $rightOperand, ?int $scale = null): string
69
    {
70 145
        $leftOperand = self::convertScientificNotationToString($leftOperand);
71 145
        $rightOperand = self::convertScientificNotationToString($rightOperand);
72
73 145
        if (self::checkIsFloat($rightOperand)) {
74 7
            if (null === $scale) {
75
                $r = self::powFractional($leftOperand, $rightOperand);
76
            } else {
77 7
                $r = self::powFractional($leftOperand, $rightOperand, $scale);
78
            }
79 145
        } else if (null === $scale) {
80 70
            $r = bcpow($leftOperand, $rightOperand);
81
        } else {
82 78
            $r = bcpow($leftOperand, $rightOperand, $scale);
83
        }
84
85 145
        return self::trimTrailingZeroes($r);
86
    }
87
88 263
    protected static function checkIsFloat(string $number): bool
89
    {
90 263
        return false !== strpos($number, '.');
91
    }
92
93 7
    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 7
        $increasedScale = $scale ?? self::getScale();
97 7
        $increasedScale *= 2;
98 7
        $decimals = explode('.', $rightOperand);
99
100 7
        return self::checkNumber(
101 7
            self::mul(
102 7
                self::exp(
103 7
                    self::mul(
104 7
                        self::log($leftOperand),
105 7
                        '0.' . $decimals[1],
106 7
                        $increasedScale
107
                    )
108
                ),
109 7
                self::pow($leftOperand, $decimals[0], $increasedScale),
110 7
                $scale
111
            )
112
        );
113
    }
114
115 38
    public static function getScale(): int
116
    {
117 38
        if (PHP_VERSION_ID >= 70300) {
118
            return bcscale();
0 ignored issues
show
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

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

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