• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

brick / math / 13541657325

26 Feb 2025 10:21AM UTC coverage: 99.651%. Remained the same
13541657325

push

github

BenMorel
Prepare for release

1142 of 1146 relevant lines covered (99.65%)

1142.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.26
/src/BigInteger.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Math;
6

7
use Brick\Math\Exception\DivisionByZeroException;
8
use Brick\Math\Exception\IntegerOverflowException;
9
use Brick\Math\Exception\MathException;
10
use Brick\Math\Exception\NegativeNumberException;
11
use Brick\Math\Exception\NumberFormatException;
12
use Brick\Math\Internal\Calculator;
13
use Override;
14

15
/**
16
 * An arbitrary-size integer.
17
 *
18
 * All methods accepting a number as a parameter accept either a BigInteger instance,
19
 * an integer, or a string representing an arbitrary size integer.
20
 *
21
 * @psalm-immutable
22
 */
23
final class BigInteger extends BigNumber
24
{
25
    /**
26
     * The value, as a string of digits with optional leading minus sign.
27
     *
28
     * No leading zeros must be present.
29
     * No leading minus sign must be present if the number is zero.
30
     */
31
    private readonly string $value;
32

33
    /**
34
     * Protected constructor. Use a factory method to obtain an instance.
35
     *
36
     * @param string $value A string of digits, with optional leading minus sign.
37
     */
38
    protected function __construct(string $value)
39
    {
40
        $this->value = $value;
18,671✔
41
    }
42

43
    /**
44
     * @psalm-pure
45
     */
46
    #[Override]
47
    protected static function from(BigNumber $number): static
48
    {
49
        return $number->toBigInteger();
13,013✔
50
    }
51

52
    /**
53
     * Creates a number from a string in a given base.
54
     *
55
     * The string can optionally be prefixed with the `+` or `-` sign.
56
     *
57
     * Bases greater than 36 are not supported by this method, as there is no clear consensus on which of the lowercase
58
     * or uppercase characters should come first. Instead, this method accepts any base up to 36, and does not
59
     * differentiate lowercase and uppercase characters, which are considered equal.
60
     *
61
     * For bases greater than 36, and/or custom alphabets, use the fromArbitraryBase() method.
62
     *
63
     * @param string $number The number to convert, in the given base.
64
     * @param int    $base   The base of the number, between 2 and 36.
65
     *
66
     * @throws NumberFormatException     If the number is empty, or contains invalid chars for the given base.
67
     * @throws \InvalidArgumentException If the base is out of range.
68
     *
69
     * @psalm-pure
70
     */
71
    public static function fromBase(string $number, int $base) : BigInteger
72
    {
73
        if ($number === '') {
2,093✔
74
            throw new NumberFormatException('The number cannot be empty.');
3✔
75
        }
76

77
        if ($base < 2 || $base > 36) {
2,090✔
78
            throw new \InvalidArgumentException(\sprintf('Base %d is not in range 2 to 36.', $base));
15✔
79
        }
80

81
        if ($number[0] === '-') {
2,075✔
82
            $sign = '-';
27✔
83
            $number = \substr($number, 1);
27✔
84
        } elseif ($number[0] === '+') {
2,048✔
85
            $sign = '';
27✔
86
            $number = \substr($number, 1);
27✔
87
        } else {
88
            $sign = '';
2,021✔
89
        }
90

91
        if ($number === '') {
2,075✔
92
            throw new NumberFormatException('The number cannot be empty.');
6✔
93
        }
94

95
        $number = \ltrim($number, '0');
2,069✔
96

97
        if ($number === '') {
2,069✔
98
            // The result will be the same in any base, avoid further calculation.
99
            return BigInteger::zero();
84✔
100
        }
101

102
        if ($number === '1') {
1,988✔
103
            // The result will be the same in any base, avoid further calculation.
104
            return new BigInteger($sign . '1');
75✔
105
        }
106

107
        $pattern = '/[^' . \substr(Calculator::ALPHABET, 0, $base) . ']/';
1,916✔
108

109
        if (\preg_match($pattern, \strtolower($number), $matches) === 1) {
1,916✔
110
            throw new NumberFormatException(\sprintf('"%s" is not a valid character in base %d.', $matches[0], $base));
111✔
111
        }
112

113
        if ($base === 10) {
1,805✔
114
            // The number is usable as is, avoid further calculation.
115
            return new BigInteger($sign . $number);
24✔
116
        }
117

118
        $result = Calculator::get()->fromBase($number, $base);
1,781✔
119

120
        return new BigInteger($sign . $result);
1,781✔
121
    }
122

123
    /**
124
     * Parses a string containing an integer in an arbitrary base, using a custom alphabet.
125
     *
126
     * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers.
127
     *
128
     * @param string $number   The number to parse.
129
     * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8.
130
     *
131
     * @throws NumberFormatException     If the given number is empty or contains invalid chars for the given alphabet.
132
     * @throws \InvalidArgumentException If the alphabet does not contain at least 2 chars.
133
     *
134
     * @psalm-pure
135
     */
136
    public static function fromArbitraryBase(string $number, string $alphabet) : BigInteger
137
    {
138
        if ($number === '') {
411✔
139
            throw new NumberFormatException('The number cannot be empty.');
3✔
140
        }
141

142
        $base = \strlen($alphabet);
408✔
143

144
        if ($base < 2) {
408✔
145
            throw new \InvalidArgumentException('The alphabet must contain at least 2 chars.');
6✔
146
        }
147

148
        $pattern = '/[^' . \preg_quote($alphabet, '/') . ']/';
402✔
149

150
        if (\preg_match($pattern, $number, $matches) === 1) {
402✔
151
            throw NumberFormatException::charNotInAlphabet($matches[0]);
24✔
152
        }
153

154
        $number = Calculator::get()->fromArbitraryBase($number, $alphabet, $base);
378✔
155

156
        return new BigInteger($number);
378✔
157
    }
158

159
    /**
160
     * Translates a string of bytes containing the binary representation of a BigInteger into a BigInteger.
161
     *
162
     * The input string is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element.
163
     *
164
     * If `$signed` is true, the input is assumed to be in two's-complement representation, and the leading bit is
165
     * interpreted as a sign bit. If `$signed` is false, the input is interpreted as an unsigned number, and the
166
     * resulting BigInteger will always be positive or zero.
167
     *
168
     * This method can be used to retrieve a number exported by `toBytes()`, as long as the `$signed` flags match.
169
     *
170
     * @param string $value  The byte string.
171
     * @param bool   $signed Whether to interpret as a signed number in two's-complement representation with a leading
172
     *                       sign bit.
173
     *
174
     * @throws NumberFormatException If the string is empty.
175
     */
176
    public static function fromBytes(string $value, bool $signed = true) : BigInteger
177
    {
178
        if ($value === '') {
1,224✔
179
            throw new NumberFormatException('The byte string must not be empty.');
3✔
180
        }
181

182
        $twosComplement = false;
1,221✔
183

184
        if ($signed) {
1,221✔
185
            $x = \ord($value[0]);
984✔
186

187
            if (($twosComplement = ($x >= 0x80))) {
984✔
188
                $value = ~$value;
906✔
189
            }
190
        }
191

192
        $number = self::fromBase(\bin2hex($value), 16);
1,221✔
193

194
        if ($twosComplement) {
1,221✔
195
            return $number->plus(1)->negated();
906✔
196
        }
197

198
        return $number;
315✔
199
    }
200

201
    /**
202
     * Generates a pseudo-random number in the range 0 to 2^numBits - 1.
203
     *
204
     * Using the default random bytes generator, this method is suitable for cryptographic use.
205
     *
206
     * @psalm-param (callable(int): string)|null $randomBytesGenerator
207
     *
208
     * @param int           $numBits              The number of bits.
209
     * @param callable|null $randomBytesGenerator A function that accepts a number of bytes as an integer, and returns a
210
     *                                            string of random bytes of the given length. Defaults to the
211
     *                                            `random_bytes()` function.
212
     *
213
     * @throws \InvalidArgumentException If $numBits is negative.
214
     */
215
    public static function randomBits(int $numBits, ?callable $randomBytesGenerator = null) : BigInteger
216
    {
217
        if ($numBits < 0) {
165✔
218
            throw new \InvalidArgumentException('The number of bits cannot be negative.');
3✔
219
        }
220

221
        if ($numBits === 0) {
162✔
222
            return BigInteger::zero();
3✔
223
        }
224

225
        if ($randomBytesGenerator === null) {
159✔
226
            $randomBytesGenerator = random_bytes(...);
×
227
        }
228

229
        /** @var int<1, max> $byteLength */
230
        $byteLength = \intdiv($numBits - 1, 8) + 1;
159✔
231

232
        $extraBits = ($byteLength * 8 - $numBits);
159✔
233
        $bitmask   = \chr(0xFF >> $extraBits);
159✔
234

235
        $randomBytes    = $randomBytesGenerator($byteLength);
159✔
236
        $randomBytes[0] = $randomBytes[0] & $bitmask;
159✔
237

238
        return self::fromBytes($randomBytes, false);
159✔
239
    }
240

241
    /**
242
     * Generates a pseudo-random number between `$min` and `$max`.
243
     *
244
     * Using the default random bytes generator, this method is suitable for cryptographic use.
245
     *
246
     * @psalm-param (callable(int): string)|null $randomBytesGenerator
247
     *
248
     * @param BigNumber|int|float|string $min                  The lower bound. Must be convertible to a BigInteger.
249
     * @param BigNumber|int|float|string $max                  The upper bound. Must be convertible to a BigInteger.
250
     * @param callable|null              $randomBytesGenerator A function that accepts a number of bytes as an integer,
251
     *                                                         and returns a string of random bytes of the given length.
252
     *                                                         Defaults to the `random_bytes()` function.
253
     *
254
     * @throws MathException If one of the parameters cannot be converted to a BigInteger,
255
     *                       or `$min` is greater than `$max`.
256
     */
257
    public static function randomRange(
258
        BigNumber|int|float|string $min,
259
        BigNumber|int|float|string $max,
260
        ?callable $randomBytesGenerator = null
261
    ) : BigInteger {
262
        $min = BigInteger::of($min);
84✔
263
        $max = BigInteger::of($max);
84✔
264

265
        if ($min->isGreaterThan($max)) {
84✔
266
            throw new MathException('$min cannot be greater than $max.');
3✔
267
        }
268

269
        if ($min->isEqualTo($max)) {
81✔
270
            return $min;
3✔
271
        }
272

273
        $diff      = $max->minus($min);
78✔
274
        $bitLength = $diff->getBitLength();
78✔
275

276
        // try until the number is in range (50% to 100% chance of success)
277
        do {
278
            $randomNumber = self::randomBits($bitLength, $randomBytesGenerator);
78✔
279
        } while ($randomNumber->isGreaterThan($diff));
78✔
280

281
        return $randomNumber->plus($min);
78✔
282
    }
283

284
    /**
285
     * Returns a BigInteger representing zero.
286
     *
287
     * @psalm-pure
288
     */
289
    public static function zero() : BigInteger
290
    {
291
        /**
292
         * @psalm-suppress ImpureStaticVariable
293
         * @var BigInteger|null $zero
294
         */
295
        static $zero;
111✔
296

297
        if ($zero === null) {
111✔
298
            $zero = new BigInteger('0');
×
299
        }
300

301
        return $zero;
111✔
302
    }
303

304
    /**
305
     * Returns a BigInteger representing one.
306
     *
307
     * @psalm-pure
308
     */
309
    public static function one() : BigInteger
310
    {
311
        /**
312
         * @psalm-suppress ImpureStaticVariable
313
         * @var BigInteger|null $one
314
         */
315
        static $one;
438✔
316

317
        if ($one === null) {
438✔
318
            $one = new BigInteger('1');
3✔
319
        }
320

321
        return $one;
438✔
322
    }
323

324
    /**
325
     * Returns a BigInteger representing ten.
326
     *
327
     * @psalm-pure
328
     */
329
    public static function ten() : BigInteger
330
    {
331
        /**
332
         * @psalm-suppress ImpureStaticVariable
333
         * @var BigInteger|null $ten
334
         */
335
        static $ten;
6✔
336

337
        if ($ten === null) {
6✔
338
            $ten = new BigInteger('10');
3✔
339
        }
340

341
        return $ten;
6✔
342
    }
343

344
    public static function gcdMultiple(BigInteger $a, BigInteger ...$n): BigInteger
345
    {
346
        $result = $a;
1,563✔
347

348
        foreach ($n as $next) {
1,563✔
349
            $result = $result->gcd($next);
1,548✔
350

351
            if ($result->isEqualTo(1)) {
1,548✔
352
                return $result;
345✔
353
            }
354
        }
355

356
        return $result;
1,218✔
357
    }
358

359
    /**
360
     * Returns the sum of this number and the given one.
361
     *
362
     * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigInteger.
363
     *
364
     * @throws MathException If the number is not valid, or is not convertible to a BigInteger.
365
     */
366
    public function plus(BigNumber|int|float|string $that) : BigInteger
367
    {
368
        $that = BigInteger::of($that);
1,560✔
369

370
        if ($that->value === '0') {
1,560✔
371
            return $this;
21✔
372
        }
373

374
        if ($this->value === '0') {
1,539✔
375
            return $that;
63✔
376
        }
377

378
        $value = Calculator::get()->add($this->value, $that->value);
1,497✔
379

380
        return new BigInteger($value);
1,497✔
381
    }
382

383
    /**
384
     * Returns the difference of this number and the given one.
385
     *
386
     * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigInteger.
387
     *
388
     * @throws MathException If the number is not valid, or is not convertible to a BigInteger.
389
     */
390
    public function minus(BigNumber|int|float|string $that) : BigInteger
391
    {
392
        $that = BigInteger::of($that);
888✔
393

394
        if ($that->value === '0') {
888✔
395
            return $this;
24✔
396
        }
397

398
        $value = Calculator::get()->sub($this->value, $that->value);
870✔
399

400
        return new BigInteger($value);
870✔
401
    }
402

403
    /**
404
     * Returns the product of this number and the given one.
405
     *
406
     * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigInteger.
407
     *
408
     * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigInteger.
409
     */
410
    public function multipliedBy(BigNumber|int|float|string $that) : BigInteger
411
    {
412
        $that = BigInteger::of($that);
1,143✔
413

414
        if ($that->value === '1') {
1,143✔
415
            return $this;
264✔
416
        }
417

418
        if ($this->value === '1') {
1,134✔
419
            return $that;
291✔
420
        }
421

422
        $value = Calculator::get()->mul($this->value, $that->value);
1,047✔
423

424
        return new BigInteger($value);
1,047✔
425
    }
426

427
    /**
428
     * Returns the result of the division of this number by the given one.
429
     *
430
     * @param BigNumber|int|float|string $that         The divisor. Must be convertible to a BigInteger.
431
     * @param RoundingMode               $roundingMode An optional rounding mode, defaults to UNNECESSARY.
432
     *
433
     * @throws MathException If the divisor is not a valid number, is not convertible to a BigInteger, is zero,
434
     *                       or RoundingMode::UNNECESSARY is used and the remainder is not zero.
435
     */
436
    public function dividedBy(BigNumber|int|float|string $that, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigInteger
437
    {
438
        $that = BigInteger::of($that);
1,980✔
439

440
        if ($that->value === '1') {
1,971✔
441
            return $this;
3✔
442
        }
443

444
        if ($that->value === '0') {
1,968✔
445
            throw DivisionByZeroException::divisionByZero();
6✔
446
        }
447

448
        $result = Calculator::get()->divRound($this->value, $that->value, $roundingMode);
1,962✔
449

450
        return new BigInteger($result);
1,869✔
451
    }
452

453
    /**
454
     * Returns this number exponentiated to the given value.
455
     *
456
     * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
457
     */
458
    public function power(int $exponent) : BigInteger
459
    {
460
        if ($exponent === 0) {
1,827✔
461
            return BigInteger::one();
21✔
462
        }
463

464
        if ($exponent === 1) {
1,806✔
465
            return $this;
198✔
466
        }
467

468
        if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
1,608✔
469
            throw new \InvalidArgumentException(\sprintf(
6✔
470
                'The exponent %d is not in the range 0 to %d.',
6✔
471
                $exponent,
6✔
472
                Calculator::MAX_POWER
6✔
473
            ));
6✔
474
        }
475

476
        return new BigInteger(Calculator::get()->pow($this->value, $exponent));
1,602✔
477
    }
478

479
    /**
480
     * Returns the quotient of the division of this number by the given one.
481
     *
482
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
483
     *
484
     * @throws DivisionByZeroException If the divisor is zero.
485
     */
486
    public function quotient(BigNumber|int|float|string $that) : BigInteger
487
    {
488
        $that = BigInteger::of($that);
969✔
489

490
        if ($that->value === '1') {
969✔
491
            return $this;
66✔
492
        }
493

494
        if ($that->value === '0') {
903✔
495
            throw DivisionByZeroException::divisionByZero();
3✔
496
        }
497

498
        $quotient = Calculator::get()->divQ($this->value, $that->value);
900✔
499

500
        return new BigInteger($quotient);
900✔
501
    }
502

503
    /**
504
     * Returns the remainder of the division of this number by the given one.
505
     *
506
     * The remainder, when non-zero, has the same sign as the dividend.
507
     *
508
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
509
     *
510
     * @throws DivisionByZeroException If the divisor is zero.
511
     */
512
    public function remainder(BigNumber|int|float|string $that) : BigInteger
513
    {
514
        $that = BigInteger::of($that);
159✔
515

516
        if ($that->value === '1') {
159✔
517
            return BigInteger::zero();
12✔
518
        }
519

520
        if ($that->value === '0') {
147✔
521
            throw DivisionByZeroException::divisionByZero();
3✔
522
        }
523

524
        $remainder = Calculator::get()->divR($this->value, $that->value);
144✔
525

526
        return new BigInteger($remainder);
144✔
527
    }
528

529
    /**
530
     * Returns the quotient and remainder of the division of this number by the given one.
531
     *
532
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
533
     *
534
     * @return BigInteger[] An array containing the quotient and the remainder.
535
     *
536
     * @psalm-return array{BigInteger, BigInteger}
537
     *
538
     * @throws DivisionByZeroException If the divisor is zero.
539
     */
540
    public function quotientAndRemainder(BigNumber|int|float|string $that) : array
541
    {
542
        $that = BigInteger::of($that);
159✔
543

544
        if ($that->value === '0') {
159✔
545
            throw DivisionByZeroException::divisionByZero();
3✔
546
        }
547

548
        [$quotient, $remainder] = Calculator::get()->divQR($this->value, $that->value);
156✔
549

550
        return [
156✔
551
            new BigInteger($quotient),
156✔
552
            new BigInteger($remainder)
156✔
553
        ];
156✔
554
    }
555

556
    /**
557
     * Returns the modulo of this number and the given one.
558
     *
559
     * The modulo operation yields the same result as the remainder operation when both operands are of the same sign,
560
     * and may differ when signs are different.
561
     *
562
     * The result of the modulo operation, when non-zero, has the same sign as the divisor.
563
     *
564
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
565
     *
566
     * @throws DivisionByZeroException If the divisor is zero.
567
     */
568
    public function mod(BigNumber|int|float|string $that) : BigInteger
569
    {
570
        $that = BigInteger::of($that);
195✔
571

572
        if ($that->value === '0') {
195✔
573
            throw DivisionByZeroException::modulusMustNotBeZero();
3✔
574
        }
575

576
        $value = Calculator::get()->mod($this->value, $that->value);
192✔
577

578
        return new BigInteger($value);
192✔
579
    }
580

581
    /**
582
     * Returns the modular multiplicative inverse of this BigInteger modulo $m.
583
     *
584
     * @throws DivisionByZeroException If $m is zero.
585
     * @throws NegativeNumberException If $m is negative.
586
     * @throws MathException           If this BigInteger has no multiplicative inverse mod m (that is, this BigInteger
587
     *                                 is not relatively prime to m).
588
     */
589
    public function modInverse(BigInteger $m) : BigInteger
590
    {
591
        if ($m->value === '0') {
66✔
592
            throw DivisionByZeroException::modulusMustNotBeZero();
6✔
593
        }
594

595
        if ($m->isNegative()) {
60✔
596
            throw new NegativeNumberException('Modulus must not be negative.');
3✔
597
        }
598

599
        if ($m->value === '1') {
57✔
600
            return BigInteger::zero();
3✔
601
        }
602

603
        $value = Calculator::get()->modInverse($this->value, $m->value);
54✔
604

605
        if ($value === null) {
54✔
606
            throw new MathException('Unable to compute the modInverse for the given modulus.');
15✔
607
        }
608

609
        return new BigInteger($value);
39✔
610
    }
611

612
    /**
613
     * Returns this number raised into power with modulo.
614
     *
615
     * This operation only works on positive numbers.
616
     *
617
     * @param BigNumber|int|float|string $exp The exponent. Must be positive or zero.
618
     * @param BigNumber|int|float|string $mod The modulus. Must be strictly positive.
619
     *
620
     * @throws NegativeNumberException If any of the operands is negative.
621
     * @throws DivisionByZeroException If the modulus is zero.
622
     */
623
    public function modPow(BigNumber|int|float|string $exp, BigNumber|int|float|string $mod) : BigInteger
624
    {
625
        $exp = BigInteger::of($exp);
47✔
626
        $mod = BigInteger::of($mod);
47✔
627

628
        if ($this->isNegative() || $exp->isNegative() || $mod->isNegative()) {
47✔
629
            throw new NegativeNumberException('The operands cannot be negative.');
9✔
630
        }
631

632
        if ($mod->isZero()) {
38✔
633
            throw DivisionByZeroException::modulusMustNotBeZero();
3✔
634
        }
635

636
        $result = Calculator::get()->modPow($this->value, $exp->value, $mod->value);
35✔
637

638
        return new BigInteger($result);
35✔
639
    }
640

641
    /**
642
     * Returns the greatest common divisor of this number and the given one.
643
     *
644
     * The GCD is always positive, unless both operands are zero, in which case it is zero.
645
     *
646
     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
647
     */
648
    public function gcd(BigNumber|int|float|string $that) : BigInteger
649
    {
650
        $that = BigInteger::of($that);
3,198✔
651

652
        if ($that->value === '0' && $this->value[0] !== '-') {
3,198✔
653
            return $this;
60✔
654
        }
655

656
        if ($this->value === '0' && $that->value[0] !== '-') {
3,138✔
657
            return $that;
30✔
658
        }
659

660
        $value = Calculator::get()->gcd($this->value, $that->value);
3,108✔
661

662
        return new BigInteger($value);
3,108✔
663
    }
664

665
    /**
666
     * Returns the integer square root number of this number, rounded down.
667
     *
668
     * The result is the largest x such that x² ≤ n.
669
     *
670
     * @throws NegativeNumberException If this number is negative.
671
     */
672
    public function sqrt() : BigInteger
673
    {
674
        if ($this->value[0] === '-') {
1,008✔
675
            throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
3✔
676
        }
677

678
        $value = Calculator::get()->sqrt($this->value);
1,005✔
679

680
        return new BigInteger($value);
1,005✔
681
    }
682

683
    /**
684
     * Returns the absolute value of this number.
685
     */
686
    public function abs() : BigInteger
687
    {
688
        return $this->isNegative() ? $this->negated() : $this;
678✔
689
    }
690

691
    /**
692
     * Returns the inverse of this number.
693
     */
694
    public function negated() : BigInteger
695
    {
696
        return new BigInteger(Calculator::get()->neg($this->value));
2,838✔
697
    }
698

699
    /**
700
     * Returns the integer bitwise-and combined with another integer.
701
     *
702
     * This method returns a negative BigInteger if and only if both operands are negative.
703
     *
704
     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
705
     */
706
    public function and(BigNumber|int|float|string $that) : BigInteger
707
    {
708
        $that = BigInteger::of($that);
153✔
709

710
        return new BigInteger(Calculator::get()->and($this->value, $that->value));
153✔
711
    }
712

713
    /**
714
     * Returns the integer bitwise-or combined with another integer.
715
     *
716
     * This method returns a negative BigInteger if and only if either of the operands is negative.
717
     *
718
     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
719
     */
720
    public function or(BigNumber|int|float|string $that) : BigInteger
721
    {
722
        $that = BigInteger::of($that);
138✔
723

724
        return new BigInteger(Calculator::get()->or($this->value, $that->value));
138✔
725
    }
726

727
    /**
728
     * Returns the integer bitwise-xor combined with another integer.
729
     *
730
     * This method returns a negative BigInteger if and only if exactly one of the operands is negative.
731
     *
732
     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
733
     */
734
    public function xor(BigNumber|int|float|string $that) : BigInteger
735
    {
736
        $that = BigInteger::of($that);
144✔
737

738
        return new BigInteger(Calculator::get()->xor($this->value, $that->value));
144✔
739
    }
740

741
    /**
742
     * Returns the bitwise-not of this BigInteger.
743
     */
744
    public function not() : BigInteger
745
    {
746
        return $this->negated()->minus(1);
57✔
747
    }
748

749
    /**
750
     * Returns the integer left shifted by a given number of bits.
751
     */
752
    public function shiftedLeft(int $distance) : BigInteger
753
    {
754
        if ($distance === 0) {
594✔
755
            return $this;
6✔
756
        }
757

758
        if ($distance < 0) {
588✔
759
            return $this->shiftedRight(- $distance);
198✔
760
        }
761

762
        return $this->multipliedBy(BigInteger::of(2)->power($distance));
390✔
763
    }
764

765
    /**
766
     * Returns the integer right shifted by a given number of bits.
767
     */
768
    public function shiftedRight(int $distance) : BigInteger
769
    {
770
        if ($distance === 0) {
1,518✔
771
            return $this;
66✔
772
        }
773

774
        if ($distance < 0) {
1,452✔
775
            return $this->shiftedLeft(- $distance);
195✔
776
        }
777

778
        $operand = BigInteger::of(2)->power($distance);
1,257✔
779

780
        if ($this->isPositiveOrZero()) {
1,257✔
781
            return $this->quotient($operand);
672✔
782
        }
783

784
        return $this->dividedBy($operand, RoundingMode::UP);
585✔
785
    }
786

787
    /**
788
     * Returns the number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit.
789
     *
790
     * For positive BigIntegers, this is equivalent to the number of bits in the ordinary binary representation.
791
     * Computes (ceil(log2(this < 0 ? -this : this+1))).
792
     */
793
    public function getBitLength() : int
794
    {
795
        if ($this->value === '0') {
321✔
796
            return 0;
12✔
797
        }
798

799
        if ($this->isNegative()) {
315✔
800
            return $this->abs()->minus(1)->getBitLength();
120✔
801
        }
802

803
        return \strlen($this->toBase(2));
309✔
804
    }
805

806
    /**
807
     * Returns the index of the rightmost (lowest-order) one bit in this BigInteger.
808
     *
809
     * Returns -1 if this BigInteger contains no one bits.
810
     */
811
    public function getLowestSetBit() : int
812
    {
813
        $n = $this;
81✔
814
        $bitLength = $this->getBitLength();
81✔
815

816
        for ($i = 0; $i <= $bitLength; $i++) {
81✔
817
            if ($n->isOdd()) {
81✔
818
                return $i;
78✔
819
            }
820

821
            $n = $n->shiftedRight(1);
51✔
822
        }
823

824
        return -1;
3✔
825
    }
826

827
    /**
828
     * Returns whether this number is even.
829
     */
830
    public function isEven() : bool
831
    {
832
        return \in_array($this->value[-1], ['0', '2', '4', '6', '8'], true);
60✔
833
    }
834

835
    /**
836
     * Returns whether this number is odd.
837
     */
838
    public function isOdd() : bool
839
    {
840
        return \in_array($this->value[-1], ['1', '3', '5', '7', '9'], true);
1,011✔
841
    }
842

843
    /**
844
     * Returns true if and only if the designated bit is set.
845
     *
846
     * Computes ((this & (1<<n)) != 0).
847
     *
848
     * @param int $n The bit to test, 0-based.
849
     *
850
     * @throws \InvalidArgumentException If the bit to test is negative.
851
     */
852
    public function testBit(int $n) : bool
853
    {
854
        if ($n < 0) {
873✔
855
            throw new \InvalidArgumentException('The bit to test cannot be negative.');
3✔
856
        }
857

858
        return $this->shiftedRight($n)->isOdd();
870✔
859
    }
860

861
    #[Override]
862
    public function compareTo(BigNumber|int|float|string $that) : int
863
    {
864
        $that = BigNumber::of($that);
2,282✔
865

866
        if ($that instanceof BigInteger) {
2,282✔
867
            return Calculator::get()->cmp($this->value, $that->value);
2,174✔
868
        }
869

870
        return - $that->compareTo($this);
108✔
871
    }
872

873
    #[Override]
874
    public function getSign() : int
875
    {
876
        return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
3,173✔
877
    }
878

879
    #[Override]
880
    public function toBigInteger() : BigInteger
881
    {
882
        return $this;
12,947✔
883
    }
884

885
    #[Override]
886
    public function toBigDecimal() : BigDecimal
887
    {
888
        return self::newBigDecimal($this->value);
3,876✔
889
    }
890

891
    #[Override]
892
    public function toBigRational() : BigRational
893
    {
894
        return self::newBigRational($this, BigInteger::one(), false);
387✔
895
    }
896

897
    #[Override]
898
    public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
899
    {
900
        return $this->toBigDecimal()->toScale($scale, $roundingMode);
9✔
901
    }
902

903
    #[Override]
904
    public function toInt() : int
905
    {
906
        $intValue = (int) $this->value;
87✔
907

908
        if ($this->value !== (string) $intValue) {
87✔
909
            throw IntegerOverflowException::toIntOverflow($this);
15✔
910
        }
911

912
        return $intValue;
72✔
913
    }
914

915
    #[Override]
916
    public function toFloat() : float
917
    {
918
        return (float) $this->value;
48✔
919
    }
920

921
    /**
922
     * Returns a string representation of this number in the given base.
923
     *
924
     * The output will always be lowercase for bases greater than 10.
925
     *
926
     * @throws \InvalidArgumentException If the base is out of range.
927
     */
928
    public function toBase(int $base) : string
929
    {
930
        if ($base === 10) {
1,293✔
931
            return $this->value;
12✔
932
        }
933

934
        if ($base < 2 || $base > 36) {
1,281✔
935
            throw new \InvalidArgumentException(\sprintf('Base %d is out of range [2, 36]', $base));
15✔
936
        }
937

938
        return Calculator::get()->toBase($this->value, $base);
1,266✔
939
    }
940

941
    /**
942
     * Returns a string representation of this number in an arbitrary base with a custom alphabet.
943
     *
944
     * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers;
945
     * a NegativeNumberException will be thrown when attempting to call this method on a negative number.
946
     *
947
     * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8.
948
     *
949
     * @throws NegativeNumberException   If this number is negative.
950
     * @throws \InvalidArgumentException If the given alphabet does not contain at least 2 chars.
951
     */
952
    public function toArbitraryBase(string $alphabet) : string
953
    {
954
        $base = \strlen($alphabet);
135✔
955

956
        if ($base < 2) {
135✔
957
            throw new \InvalidArgumentException('The alphabet must contain at least 2 chars.');
6✔
958
        }
959

960
        if ($this->value[0] === '-') {
129✔
961
            throw new NegativeNumberException(__FUNCTION__ . '() does not support negative numbers.');
3✔
962
        }
963

964
        return Calculator::get()->toArbitraryBase($this->value, $alphabet, $base);
126✔
965
    }
966

967
    /**
968
     * Returns a string of bytes containing the binary representation of this BigInteger.
969
     *
970
     * The string is in big-endian byte-order: the most significant byte is in the zeroth element.
971
     *
972
     * If `$signed` is true, the output will be in two's-complement representation, and a sign bit will be prepended to
973
     * the output. If `$signed` is false, no sign bit will be prepended, and this method will throw an exception if the
974
     * number is negative.
975
     *
976
     * The string will contain the minimum number of bytes required to represent this BigInteger, including a sign bit
977
     * if `$signed` is true.
978
     *
979
     * This representation is compatible with the `fromBytes()` factory method, as long as the `$signed` flags match.
980
     *
981
     * @param bool $signed Whether to output a signed number in two's-complement representation with a leading sign bit.
982
     *
983
     * @throws NegativeNumberException If $signed is false, and the number is negative.
984
     */
985
    public function toBytes(bool $signed = true) : string
986
    {
987
        if (! $signed && $this->isNegative()) {
534✔
988
            throw new NegativeNumberException('Cannot convert a negative number to a byte string when $signed is false.');
3✔
989
        }
990

991
        $hex = $this->abs()->toBase(16);
531✔
992

993
        if (\strlen($hex) % 2 !== 0) {
531✔
994
            $hex = '0' . $hex;
219✔
995
        }
996

997
        $baseHexLength = \strlen($hex);
531✔
998

999
        if ($signed) {
531✔
1000
            if ($this->isNegative()) {
492✔
1001
                $bin = \hex2bin($hex);
453✔
1002
                assert($bin !== false);
1003

1004
                $hex = \bin2hex(~$bin);
453✔
1005
                $hex = self::fromBase($hex, 16)->plus(1)->toBase(16);
453✔
1006

1007
                $hexLength = \strlen($hex);
453✔
1008

1009
                if ($hexLength < $baseHexLength) {
453✔
1010
                    $hex = \str_repeat('0', $baseHexLength - $hexLength) . $hex;
72✔
1011
                }
1012

1013
                if ($hex[0] < '8') {
453✔
1014
                    $hex = 'FF' . $hex;
114✔
1015
                }
1016
            } else {
1017
                if ($hex[0] >= '8') {
39✔
1018
                    $hex = '00' . $hex;
21✔
1019
                }
1020
            }
1021
        }
1022

1023
        return \hex2bin($hex);
531✔
1024
    }
1025

1026
    #[Override]
1027
    public function __toString() : string
1028
    {
1029
        return $this->value;
12,783✔
1030
    }
1031

1032
    /**
1033
     * This method is required for serializing the object and SHOULD NOT be accessed directly.
1034
     *
1035
     * @internal
1036
     *
1037
     * @return array{value: string}
1038
     */
1039
    public function __serialize(): array
1040
    {
1041
        return ['value' => $this->value];
6✔
1042
    }
1043

1044
    /**
1045
     * This method is only here to allow unserializing the object and cannot be accessed directly.
1046
     *
1047
     * @internal
1048
     * @psalm-suppress RedundantPropertyInitializationCheck
1049
     *
1050
     * @param array{value: string} $data
1051
     *
1052
     * @throws \LogicException
1053
     */
1054
    public function __unserialize(array $data): void
1055
    {
1056
        if (isset($this->value)) {
9✔
1057
            throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
3✔
1058
        }
1059

1060
        $this->value = $data['value'];
6✔
1061
    }
1062
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc