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

brick / math / 21416478676

27 Jan 2026 10:20PM UTC coverage: 99.423% (+0.01%) from 99.412%
21416478676

push

github

BenMorel
Remove deprecated BigRational::simplified()

1207 of 1214 relevant lines covered (99.42%)

2597.78 hits per line

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

98.98
/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\Exception\RoundingNecessaryException;
13
use Brick\Math\Internal\Calculator;
14
use Brick\Math\Internal\CalculatorRegistry;
15
use InvalidArgumentException;
16
use LogicException;
17
use Override;
18

19
use function assert;
20
use function bin2hex;
21
use function chr;
22
use function filter_var;
23
use function hex2bin;
24
use function in_array;
25
use function intdiv;
26
use function ltrim;
27
use function ord;
28
use function preg_match;
29
use function preg_quote;
30
use function random_bytes;
31
use function sprintf;
32
use function str_repeat;
33
use function strlen;
34
use function strtolower;
35
use function substr;
36

37
use const FILTER_VALIDATE_INT;
38

39
/**
40
 * An arbitrary-size integer.
41
 *
42
 * All methods accepting a number as a parameter accept either a BigInteger instance,
43
 * an integer, or a string representing an arbitrary size integer.
44
 */
45
final readonly class BigInteger extends BigNumber
46
{
47
    /**
48
     * The value, as a string of digits with optional leading minus sign.
49
     *
50
     * No leading zeros must be present.
51
     * No leading minus sign must be present if the number is zero.
52
     */
53
    private string $value;
54

55
    /**
56
     * Protected constructor. Use a factory method to obtain an instance.
57
     *
58
     * @param string $value A string of digits, with optional leading minus sign.
59
     *
60
     * @pure
61
     */
62
    protected function __construct(string $value)
63
    {
64
        $this->value = $value;
30,653✔
65
    }
66

67
    /**
68
     * Creates a number from a string in a given base.
69
     *
70
     * The string can optionally be prefixed with the `+` or `-` sign.
71
     *
72
     * Bases greater than 36 are not supported by this method, as there is no clear consensus on which of the lowercase
73
     * or uppercase characters should come first. Instead, this method accepts any base up to 36, and does not
74
     * differentiate lowercase and uppercase characters, which are considered equal.
75
     *
76
     * For bases greater than 36, and/or custom alphabets, use the fromArbitraryBase() method.
77
     *
78
     * @param string $number The number to convert, in the given base.
79
     * @param int    $base   The base of the number, between 2 and 36.
80
     *
81
     * @throws NumberFormatException    If the number is empty, or contains invalid chars for the given base.
82
     * @throws InvalidArgumentException If the base is out of range.
83
     *
84
     * @pure
85
     */
86
    public static function fromBase(string $number, int $base): BigInteger
87
    {
88
        if ($number === '') {
2,093✔
89
            throw new NumberFormatException('The number cannot be empty.');
3✔
90
        }
91

92
        if ($base < 2 || $base > 36) {
2,090✔
93
            throw new InvalidArgumentException(sprintf('Base %d is not in range 2 to 36.', $base));
15✔
94
        }
95

96
        if ($number[0] === '-') {
2,075✔
97
            $sign = '-';
27✔
98
            $number = substr($number, 1);
27✔
99
        } elseif ($number[0] === '+') {
2,048✔
100
            $sign = '';
27✔
101
            $number = substr($number, 1);
27✔
102
        } else {
103
            $sign = '';
2,021✔
104
        }
105

106
        if ($number === '') {
2,075✔
107
            throw new NumberFormatException('The number cannot be empty.');
6✔
108
        }
109

110
        $number = ltrim($number, '0');
2,069✔
111

112
        if ($number === '') {
2,069✔
113
            // The result will be the same in any base, avoid further calculation.
114
            return BigInteger::zero();
84✔
115
        }
116

117
        if ($number === '1') {
1,988✔
118
            // The result will be the same in any base, avoid further calculation.
119
            return new BigInteger($sign . '1');
75✔
120
        }
121

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

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

128
        if ($base === 10) {
1,805✔
129
            // The number is usable as is, avoid further calculation.
130
            return new BigInteger($sign . $number);
24✔
131
        }
132

133
        $result = CalculatorRegistry::get()->fromBase($number, $base);
1,781✔
134

135
        return new BigInteger($sign . $result);
1,781✔
136
    }
137

138
    /**
139
     * Parses a string containing an integer in an arbitrary base, using a custom alphabet.
140
     *
141
     * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers.
142
     *
143
     * @param string $number   The number to parse.
144
     * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8.
145
     *
146
     * @throws NumberFormatException    If the given number is empty or contains invalid chars for the given alphabet.
147
     * @throws InvalidArgumentException If the alphabet does not contain at least 2 chars.
148
     *
149
     * @pure
150
     */
151
    public static function fromArbitraryBase(string $number, string $alphabet): BigInteger
152
    {
153
        if ($number === '') {
411✔
154
            throw new NumberFormatException('The number cannot be empty.');
3✔
155
        }
156

157
        $base = strlen($alphabet);
408✔
158

159
        if ($base < 2) {
408✔
160
            throw new InvalidArgumentException('The alphabet must contain at least 2 chars.');
6✔
161
        }
162

163
        $pattern = '/[^' . preg_quote($alphabet, '/') . ']/';
402✔
164

165
        if (preg_match($pattern, $number, $matches) === 1) {
402✔
166
            throw NumberFormatException::charNotInAlphabet($matches[0]);
24✔
167
        }
168

169
        $number = CalculatorRegistry::get()->fromArbitraryBase($number, $alphabet, $base);
378✔
170

171
        return new BigInteger($number);
378✔
172
    }
173

174
    /**
175
     * Translates a string of bytes containing the binary representation of a BigInteger into a BigInteger.
176
     *
177
     * The input string is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element.
178
     *
179
     * If `$signed` is true, the input is assumed to be in two's-complement representation, and the leading bit is
180
     * interpreted as a sign bit. If `$signed` is false, the input is interpreted as an unsigned number, and the
181
     * resulting BigInteger will always be positive or zero.
182
     *
183
     * This method can be used to retrieve a number exported by `toBytes()`, as long as the `$signed` flags match.
184
     *
185
     * @param string $value  The byte string.
186
     * @param bool   $signed Whether to interpret as a signed number in two's-complement representation with a leading
187
     *                       sign bit.
188
     *
189
     * @throws NumberFormatException If the string is empty.
190
     *
191
     * @pure
192
     */
193
    public static function fromBytes(string $value, bool $signed = true): BigInteger
194
    {
195
        if ($value === '') {
1,224✔
196
            throw new NumberFormatException('The byte string must not be empty.');
3✔
197
        }
198

199
        $twosComplement = false;
1,221✔
200

201
        if ($signed) {
1,221✔
202
            $x = ord($value[0]);
984✔
203

204
            if (($twosComplement = ($x >= 0x80))) {
984✔
205
                $value = ~$value;
906✔
206
            }
207
        }
208

209
        $number = self::fromBase(bin2hex($value), 16);
1,221✔
210

211
        if ($twosComplement) {
1,221✔
212
            return $number->plus(1)->negated();
906✔
213
        }
214

215
        return $number;
315✔
216
    }
217

218
    /**
219
     * Generates a pseudo-random number in the range 0 to 2^numBits - 1.
220
     *
221
     * Using the default random bytes generator, this method is suitable for cryptographic use.
222
     *
223
     * @param int                          $numBits              The number of bits.
224
     * @param (callable(int): string)|null $randomBytesGenerator A function that accepts a number of bytes, and returns
225
     *                                                           a string of random bytes of the given length. Defaults
226
     *                                                           to the `random_bytes()` function.
227
     *
228
     * @throws InvalidArgumentException If $numBits is negative.
229
     */
230
    public static function randomBits(int $numBits, ?callable $randomBytesGenerator = null): BigInteger
231
    {
232
        if ($numBits < 0) {
165✔
233
            throw new InvalidArgumentException('The number of bits cannot be negative.');
3✔
234
        }
235

236
        if ($numBits === 0) {
162✔
237
            return BigInteger::zero();
3✔
238
        }
239

240
        if ($randomBytesGenerator === null) {
159✔
241
            $randomBytesGenerator = random_bytes(...);
×
242
        }
243

244
        /** @var int<1, max> $byteLength */
245
        $byteLength = intdiv($numBits - 1, 8) + 1;
159✔
246

247
        $extraBits = ($byteLength * 8 - $numBits);
159✔
248
        $bitmask = chr(0xFF >> $extraBits);
159✔
249

250
        $randomBytes = $randomBytesGenerator($byteLength);
159✔
251
        $randomBytes[0] = $randomBytes[0] & $bitmask;
159✔
252

253
        return self::fromBytes($randomBytes, false);
159✔
254
    }
255

256
    /**
257
     * Generates a pseudo-random number between `$min` and `$max`.
258
     *
259
     * Using the default random bytes generator, this method is suitable for cryptographic use.
260
     *
261
     * @param BigNumber|int|float|string   $min                  The lower bound. Must be convertible to a BigInteger.
262
     * @param BigNumber|int|float|string   $max                  The upper bound. Must be convertible to a BigInteger.
263
     * @param (callable(int): string)|null $randomBytesGenerator A function that accepts a number of bytes, and returns
264
     *                                                           a string of random bytes of the given length. Defaults
265
     *                                                           to the `random_bytes()` function.
266
     *
267
     * @throws MathException If one of the parameters cannot be converted to a BigInteger,
268
     *                       or `$min` is greater than `$max`.
269
     */
270
    public static function randomRange(
271
        BigNumber|int|float|string $min,
272
        BigNumber|int|float|string $max,
273
        ?callable $randomBytesGenerator = null,
274
    ): BigInteger {
275
        $min = BigInteger::of($min);
84✔
276
        $max = BigInteger::of($max);
84✔
277

278
        if ($min->isGreaterThan($max)) {
84✔
279
            throw new MathException('$min cannot be greater than $max.');
3✔
280
        }
281

282
        if ($min->isEqualTo($max)) {
81✔
283
            return $min;
3✔
284
        }
285

286
        $diff = $max->minus($min);
78✔
287
        $bitLength = $diff->getBitLength();
78✔
288

289
        // try until the number is in range (50% to 100% chance of success)
290
        do {
291
            $randomNumber = self::randomBits($bitLength, $randomBytesGenerator);
78✔
292
        } while ($randomNumber->isGreaterThan($diff));
78✔
293

294
        return $randomNumber->plus($min);
78✔
295
    }
296

297
    /**
298
     * Returns a BigInteger representing zero.
299
     *
300
     * @pure
301
     */
302
    public static function zero(): BigInteger
303
    {
304
        /** @var BigInteger|null $zero */
305
        static $zero;
132✔
306

307
        if ($zero === null) {
132✔
308
            $zero = new BigInteger('0');
×
309
        }
310

311
        return $zero;
132✔
312
    }
313

314
    /**
315
     * Returns a BigInteger representing one.
316
     *
317
     * @pure
318
     */
319
    public static function one(): BigInteger
320
    {
321
        /** @var BigInteger|null $one */
322
        static $one;
516✔
323

324
        if ($one === null) {
516✔
325
            $one = new BigInteger('1');
×
326
        }
327

328
        return $one;
516✔
329
    }
330

331
    /**
332
     * Returns a BigInteger representing ten.
333
     *
334
     * @pure
335
     */
336
    public static function ten(): BigInteger
337
    {
338
        /** @var BigInteger|null $ten */
339
        static $ten;
6✔
340

341
        if ($ten === null) {
6✔
342
            $ten = new BigInteger('10');
3✔
343
        }
344

345
        return $ten;
6✔
346
    }
347

348
    /**
349
     * @param BigNumber|int|float|string $a    The first number. Must be convertible to a BigInteger.
350
     * @param BigNumber|int|float|string ...$n The subsequent numbers. Must be convertible to BigInteger.
351
     *
352
     * @pure
353
     */
354
    public static function gcdAll(BigNumber|int|float|string $a, BigNumber|int|float|string ...$n): BigInteger
355
    {
356
        $result = BigInteger::of($a);
1,566✔
357

358
        foreach ($n as $next) {
1,566✔
359
            $result = $result->gcd(BigInteger::of($next));
1,551✔
360

361
            if ($result->isEqualTo(1)) {
1,551✔
362
                return $result;
345✔
363
            }
364
        }
365

366
        return $result;
1,221✔
367
    }
368

369
    /**
370
     * Returns the sum of this number and the given one.
371
     *
372
     * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigInteger.
373
     *
374
     * @throws MathException If the number is not valid, or is not convertible to a BigInteger.
375
     *
376
     * @pure
377
     */
378
    public function plus(BigNumber|int|float|string $that): BigInteger
379
    {
380
        $that = BigInteger::of($that);
1,605✔
381

382
        if ($that->value === '0') {
1,605✔
383
            return $this;
30✔
384
        }
385

386
        if ($this->value === '0') {
1,575✔
387
            return $that;
69✔
388
        }
389

390
        $value = CalculatorRegistry::get()->add($this->value, $that->value);
1,527✔
391

392
        return new BigInteger($value);
1,527✔
393
    }
394

395
    /**
396
     * Returns the difference of this number and the given one.
397
     *
398
     * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigInteger.
399
     *
400
     * @throws MathException If the number is not valid, or is not convertible to a BigInteger.
401
     *
402
     * @pure
403
     */
404
    public function minus(BigNumber|int|float|string $that): BigInteger
405
    {
406
        $that = BigInteger::of($that);
963✔
407

408
        if ($that->value === '0') {
963✔
409
            return $this;
30✔
410
        }
411

412
        $value = CalculatorRegistry::get()->sub($this->value, $that->value);
942✔
413

414
        return new BigInteger($value);
942✔
415
    }
416

417
    /**
418
     * Returns the product of this number and the given one.
419
     *
420
     * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigInteger.
421
     *
422
     * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigInteger.
423
     *
424
     * @pure
425
     */
426
    public function multipliedBy(BigNumber|int|float|string $that): BigInteger
427
    {
428
        $that = BigInteger::of($that);
1,242✔
429

430
        if ($that->value === '1') {
1,242✔
431
            return $this;
393✔
432
        }
433

434
        if ($this->value === '1') {
1,179✔
435
            return $that;
354✔
436
        }
437

438
        $value = CalculatorRegistry::get()->mul($this->value, $that->value);
1,089✔
439

440
        return new BigInteger($value);
1,089✔
441
    }
442

443
    /**
444
     * Returns the result of the division of this number by the given one.
445
     *
446
     * @param BigNumber|int|float|string $that         The divisor. Must be convertible to a BigInteger.
447
     * @param RoundingMode               $roundingMode An optional rounding mode, defaults to Unnecessary.
448
     *
449
     * @throws MathException If the divisor is not a valid number, is not convertible to a BigInteger, is zero,
450
     *                       or RoundingMode::Unnecessary is used and the remainder is not zero.
451
     *
452
     * @pure
453
     */
454
    public function dividedBy(BigNumber|int|float|string $that, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigInteger
455
    {
456
        $that = BigInteger::of($that);
1,980✔
457

458
        if ($that->value === '1') {
1,971✔
459
            return $this;
3✔
460
        }
461

462
        if ($that->value === '0') {
1,968✔
463
            throw DivisionByZeroException::divisionByZero();
6✔
464
        }
465

466
        $result = CalculatorRegistry::get()->divRound($this->value, $that->value, $roundingMode);
1,962✔
467

468
        return new BigInteger($result);
1,869✔
469
    }
470

471
    /**
472
     * Limits (clamps) this number between the given minimum and maximum values.
473
     *
474
     * If the number is lower than $min, returns a copy of $min.
475
     * If the number is greater than $max, returns a copy of $max.
476
     * Otherwise, returns this number unchanged.
477
     *
478
     * @param BigNumber|int|float|string $min The minimum. Must be convertible to a BigInteger.
479
     * @param BigNumber|int|float|string $max The maximum. Must be convertible to a BigInteger.
480
     *
481
     * @throws MathException If min/max are not convertible to a BigInteger.
482
     */
483
    public function clamp(BigNumber|int|float|string $min, BigNumber|int|float|string $max): BigInteger
484
    {
485
        if ($this->isLessThan($min)) {
30✔
486
            return BigInteger::of($min);
9✔
487
        } elseif ($this->isGreaterThan($max)) {
21✔
488
            return BigInteger::of($max);
3✔
489
        }
490

491
        return $this;
18✔
492
    }
493

494
    /**
495
     * Returns this number exponentiated to the given value.
496
     *
497
     * @throws InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
498
     *
499
     * @pure
500
     */
501
    public function power(int $exponent): BigInteger
502
    {
503
        if ($exponent === 0) {
1,851✔
504
            return BigInteger::one();
27✔
505
        }
506

507
        if ($exponent === 1) {
1,824✔
508
            return $this;
204✔
509
        }
510

511
        if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
1,620✔
512
            throw new InvalidArgumentException(sprintf(
6✔
513
                'The exponent %d is not in the range 0 to %d.',
6✔
514
                $exponent,
6✔
515
                Calculator::MAX_POWER,
6✔
516
            ));
6✔
517
        }
518

519
        return new BigInteger(CalculatorRegistry::get()->pow($this->value, $exponent));
1,614✔
520
    }
521

522
    /**
523
     * Returns the quotient of the division of this number by the given one.
524
     *
525
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
526
     *
527
     * @throws DivisionByZeroException If the divisor is zero.
528
     *
529
     * @pure
530
     */
531
    public function quotient(BigNumber|int|float|string $that): BigInteger
532
    {
533
        $that = BigInteger::of($that);
2,709✔
534

535
        if ($that->value === '1') {
2,709✔
536
            return $this;
1,386✔
537
        }
538

539
        if ($that->value === '0') {
1,842✔
540
            throw DivisionByZeroException::divisionByZero();
3✔
541
        }
542

543
        $quotient = CalculatorRegistry::get()->divQ($this->value, $that->value);
1,839✔
544

545
        return new BigInteger($quotient);
1,839✔
546
    }
547

548
    /**
549
     * Returns the remainder of the division of this number by the given one.
550
     *
551
     * The remainder, when non-zero, has the same sign as the dividend.
552
     *
553
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
554
     *
555
     * @throws DivisionByZeroException If the divisor is zero.
556
     *
557
     * @pure
558
     */
559
    public function remainder(BigNumber|int|float|string $that): BigInteger
560
    {
561
        $that = BigInteger::of($that);
228✔
562

563
        if ($that->value === '1') {
228✔
564
            return BigInteger::zero();
33✔
565
        }
566

567
        if ($that->value === '0') {
195✔
568
            throw DivisionByZeroException::divisionByZero();
3✔
569
        }
570

571
        $remainder = CalculatorRegistry::get()->divR($this->value, $that->value);
192✔
572

573
        return new BigInteger($remainder);
192✔
574
    }
575

576
    /**
577
     * Returns the quotient and remainder of the division of this number by the given one.
578
     *
579
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
580
     *
581
     * @return array{BigInteger, BigInteger} An array containing the quotient and the remainder.
582
     *
583
     * @throws DivisionByZeroException If the divisor is zero.
584
     *
585
     * @pure
586
     */
587
    public function quotientAndRemainder(BigNumber|int|float|string $that): array
588
    {
589
        $that = BigInteger::of($that);
147✔
590

591
        if ($that->value === '0') {
147✔
592
            throw DivisionByZeroException::divisionByZero();
3✔
593
        }
594

595
        [$quotient, $remainder] = CalculatorRegistry::get()->divQR($this->value, $that->value);
144✔
596

597
        return [
144✔
598
            new BigInteger($quotient),
144✔
599
            new BigInteger($remainder),
144✔
600
        ];
144✔
601
    }
602

603
    /**
604
     * Returns the modulo of this number and the given one.
605
     *
606
     * The modulo operation yields the same result as the remainder operation when both operands are of the same sign,
607
     * and may differ when signs are different.
608
     *
609
     * The result of the modulo operation, when non-zero, has the same sign as the divisor.
610
     *
611
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
612
     *
613
     * @throws DivisionByZeroException If the divisor is zero.
614
     *
615
     * @pure
616
     */
617
    public function mod(BigNumber|int|float|string $that): BigInteger
618
    {
619
        $that = BigInteger::of($that);
195✔
620

621
        if ($that->value === '0') {
195✔
622
            throw DivisionByZeroException::modulusMustNotBeZero();
3✔
623
        }
624

625
        $value = CalculatorRegistry::get()->mod($this->value, $that->value);
192✔
626

627
        return new BigInteger($value);
192✔
628
    }
629

630
    /**
631
     * Returns the modular multiplicative inverse of this BigInteger modulo $modulus.
632
     *
633
     * @param BigNumber|int|float|string $modulus The modulus. Must be convertible to a BigInteger.
634
     *
635
     * @throws DivisionByZeroException If $modulus is zero.
636
     * @throws NegativeNumberException If $modulus is negative.
637
     * @throws MathException           If this BigInteger has no multiplicative inverse mod m (that is, this BigInteger
638
     *                                 is not relatively prime to m).
639
     *
640
     * @pure
641
     */
642
    public function modInverse(BigNumber|int|float|string $modulus): BigInteger
643
    {
644
        $modulus = BigInteger::of($modulus);
75✔
645

646
        if ($modulus->value === '0') {
75✔
647
            throw DivisionByZeroException::modulusMustNotBeZero();
6✔
648
        }
649

650
        if ($modulus->isNegative()) {
69✔
651
            throw new NegativeNumberException('Modulus must not be negative.');
3✔
652
        }
653

654
        if ($modulus->value === '1') {
66✔
655
            return BigInteger::zero();
3✔
656
        }
657

658
        $value = CalculatorRegistry::get()->modInverse($this->value, $modulus->value);
63✔
659

660
        if ($value === null) {
63✔
661
            throw new MathException('Unable to compute the modInverse for the given modulus.');
15✔
662
        }
663

664
        return new BigInteger($value);
48✔
665
    }
666

667
    /**
668
     * Returns this number raised into power with modulo.
669
     *
670
     * This operation only works on positive numbers.
671
     *
672
     * @param BigNumber|int|float|string $exponent The exponent. Must be positive or zero.
673
     * @param BigNumber|int|float|string $modulus  The modulus. Must be strictly positive.
674
     *
675
     * @throws NegativeNumberException If any of the operands is negative.
676
     * @throws DivisionByZeroException If the modulus is zero.
677
     *
678
     * @pure
679
     */
680
    public function modPow(BigNumber|int|float|string $exponent, BigNumber|int|float|string $modulus): BigInteger
681
    {
682
        $exponent = BigInteger::of($exponent);
47✔
683
        $modulus = BigInteger::of($modulus);
47✔
684

685
        if ($this->isNegative() || $exponent->isNegative() || $modulus->isNegative()) {
47✔
686
            throw new NegativeNumberException('The operands cannot be negative.');
9✔
687
        }
688

689
        if ($modulus->isZero()) {
38✔
690
            throw DivisionByZeroException::modulusMustNotBeZero();
3✔
691
        }
692

693
        $result = CalculatorRegistry::get()->modPow($this->value, $exponent->value, $modulus->value);
35✔
694

695
        return new BigInteger($result);
35✔
696
    }
697

698
    /**
699
     * Returns the greatest common divisor of this number and the given one.
700
     *
701
     * The GCD is always positive, unless both operands are zero, in which case it is zero.
702
     *
703
     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
704
     *
705
     * @pure
706
     */
707
    public function gcd(BigNumber|int|float|string $that): BigInteger
708
    {
709
        $that = BigInteger::of($that);
4,941✔
710

711
        if ($that->value === '0' && $this->value[0] !== '-') {
4,941✔
712
            return $this;
60✔
713
        }
714

715
        if ($this->value === '0' && $that->value[0] !== '-') {
4,881✔
716
            return $that;
273✔
717
        }
718

719
        $value = CalculatorRegistry::get()->gcd($this->value, $that->value);
4,794✔
720

721
        return new BigInteger($value);
4,794✔
722
    }
723

724
    /**
725
     * Returns the integer square root of this number, rounded according to the given rounding mode.
726
     *
727
     * @param RoundingMode $roundingMode The rounding mode to use, defaults to Unnecessary.
728
     *
729
     * @throws NegativeNumberException    If this number is negative.
730
     * @throws RoundingNecessaryException If RoundingMode::Unnecessary is used, and the number is not a perfect square.
731
     *
732
     * @pure
733
     */
734
    public function sqrt(RoundingMode $roundingMode = RoundingMode::Unnecessary): BigInteger
735
    {
736
        if ($this->value[0] === '-') {
10,623✔
737
            throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
3✔
738
        }
739

740
        $calculator = CalculatorRegistry::get();
10,620✔
741

742
        $sqrt = $calculator->sqrt($this->value);
10,620✔
743

744
        // For Down and Floor (equivalent for non-negative numbers), return floor sqrt
745
        if ($roundingMode === RoundingMode::Down || $roundingMode === RoundingMode::Floor) {
10,620✔
746
            return new BigInteger($sqrt);
2,130✔
747
        }
748

749
        // Check if the sqrt is exact
750
        $s2 = $calculator->mul($sqrt, $sqrt);
8,490✔
751
        $remainder = $calculator->sub($this->value, $s2);
8,490✔
752

753
        if ($remainder === '0') {
8,490✔
754
            // sqrt is exact
755
            return new BigInteger($sqrt);
5,385✔
756
        }
757

758
        // sqrt is not exact
759
        if ($roundingMode === RoundingMode::Unnecessary) {
3,105✔
760
            throw RoundingNecessaryException::roundingNecessary();
390✔
761
        }
762

763
        // For Up and Ceiling (equivalent for non-negative numbers), round up
764
        if ($roundingMode === RoundingMode::Up || $roundingMode === RoundingMode::Ceiling) {
2,715✔
765
            return new BigInteger($calculator->add($sqrt, '1'));
780✔
766
        }
767

768
        // For Half* modes, compare our number to the midpoint of the interval [s², (s+1)²[.
769
        // The midpoint is s² + s + 0.5. Comparing n >= s² + s + 0.5 with remainder = n − s²
770
        // is equivalent to comparing 2*remainder >= 2*s + 1.
771
        $twoRemainder = $calculator->mul($remainder, '2');
1,935✔
772
        $threshold = $calculator->add($calculator->mul($sqrt, '2'), '1');
1,935✔
773
        $cmp = $calculator->cmp($twoRemainder, $threshold);
1,935✔
774

775
        // We're supposed to increment (round up) when:
776
        //   - HalfUp, HalfCeiling => $cmp >= 0
777
        //   - HalfDown, HalfFloor => $cmp > 0
778
        //   - HalfEven => $cmp > 0 || ($cmp === 0 && $sqrt % 2 === 1)
779
        // But 2*remainder is always even and 2*s + 1 is always odd, so $cmp is never zero.
780
        // Therefore, all Half* modes simplify to:
781
        if ($cmp > 0) {
1,935✔
782
            $sqrt = $calculator->add($sqrt, '1');
990✔
783
        }
784

785
        return new BigInteger($sqrt);
1,935✔
786
    }
787

788
    /**
789
     * Returns the absolute value of this number.
790
     *
791
     * @pure
792
     */
793
    public function abs(): BigInteger
794
    {
795
        return $this->isNegative() ? $this->negated() : $this;
678✔
796
    }
797

798
    /**
799
     * Returns the inverse of this number.
800
     *
801
     * @pure
802
     */
803
    public function negated(): BigInteger
804
    {
805
        return new BigInteger(CalculatorRegistry::get()->neg($this->value));
2,850✔
806
    }
807

808
    /**
809
     * Returns the integer bitwise-and combined with another integer.
810
     *
811
     * This method returns a negative BigInteger if and only if both operands are negative.
812
     *
813
     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
814
     *
815
     * @pure
816
     */
817
    public function and(BigNumber|int|float|string $that): BigInteger
818
    {
819
        $that = BigInteger::of($that);
153✔
820

821
        return new BigInteger(CalculatorRegistry::get()->and($this->value, $that->value));
153✔
822
    }
823

824
    /**
825
     * Returns the integer bitwise-or combined with another integer.
826
     *
827
     * This method returns a negative BigInteger if and only if either of the operands is negative.
828
     *
829
     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
830
     *
831
     * @pure
832
     */
833
    public function or(BigNumber|int|float|string $that): BigInteger
834
    {
835
        $that = BigInteger::of($that);
138✔
836

837
        return new BigInteger(CalculatorRegistry::get()->or($this->value, $that->value));
138✔
838
    }
839

840
    /**
841
     * Returns the integer bitwise-xor combined with another integer.
842
     *
843
     * This method returns a negative BigInteger if and only if exactly one of the operands is negative.
844
     *
845
     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
846
     *
847
     * @pure
848
     */
849
    public function xor(BigNumber|int|float|string $that): BigInteger
850
    {
851
        $that = BigInteger::of($that);
144✔
852

853
        return new BigInteger(CalculatorRegistry::get()->xor($this->value, $that->value));
144✔
854
    }
855

856
    /**
857
     * Returns the bitwise-not of this BigInteger.
858
     *
859
     * @pure
860
     */
861
    public function not(): BigInteger
862
    {
863
        return $this->negated()->minus(1);
57✔
864
    }
865

866
    /**
867
     * Returns the integer left shifted by a given number of bits.
868
     *
869
     * @pure
870
     */
871
    public function shiftedLeft(int $bits): BigInteger
872
    {
873
        if ($bits === 0) {
594✔
874
            return $this;
6✔
875
        }
876

877
        if ($bits < 0) {
588✔
878
            return $this->shiftedRight(-$bits);
198✔
879
        }
880

881
        return $this->multipliedBy(BigInteger::of(2)->power($bits));
390✔
882
    }
883

884
    /**
885
     * Returns the integer right shifted by a given number of bits.
886
     *
887
     * @pure
888
     */
889
    public function shiftedRight(int $bits): BigInteger
890
    {
891
        if ($bits === 0) {
1,518✔
892
            return $this;
66✔
893
        }
894

895
        if ($bits < 0) {
1,452✔
896
            return $this->shiftedLeft(-$bits);
195✔
897
        }
898

899
        $operand = BigInteger::of(2)->power($bits);
1,257✔
900

901
        if ($this->isPositiveOrZero()) {
1,257✔
902
            return $this->quotient($operand);
672✔
903
        }
904

905
        return $this->dividedBy($operand, RoundingMode::Up);
585✔
906
    }
907

908
    /**
909
     * Returns the number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit.
910
     *
911
     * For positive BigIntegers, this is equivalent to the number of bits in the ordinary binary representation.
912
     * Computes (ceil(log2(this < 0 ? -this : this+1))).
913
     *
914
     * @pure
915
     */
916
    public function getBitLength(): int
917
    {
918
        if ($this->value === '0') {
321✔
919
            return 0;
12✔
920
        }
921

922
        if ($this->isNegative()) {
315✔
923
            return $this->abs()->minus(1)->getBitLength();
120✔
924
        }
925

926
        return strlen($this->toBase(2));
309✔
927
    }
928

929
    /**
930
     * Returns the index of the rightmost (lowest-order) one bit in this BigInteger.
931
     *
932
     * Returns -1 if this BigInteger contains no one bits.
933
     *
934
     * @pure
935
     */
936
    public function getLowestSetBit(): int
937
    {
938
        $n = $this;
81✔
939
        $bitLength = $this->getBitLength();
81✔
940

941
        for ($i = 0; $i <= $bitLength; $i++) {
81✔
942
            if ($n->isOdd()) {
81✔
943
                return $i;
78✔
944
            }
945

946
            $n = $n->shiftedRight(1);
51✔
947
        }
948

949
        return -1;
3✔
950
    }
951

952
    /**
953
     * Returns whether this number is even.
954
     *
955
     * @pure
956
     */
957
    public function isEven(): bool
958
    {
959
        return in_array($this->value[-1], ['0', '2', '4', '6', '8'], true);
60✔
960
    }
961

962
    /**
963
     * Returns whether this number is odd.
964
     *
965
     * @pure
966
     */
967
    public function isOdd(): bool
968
    {
969
        return in_array($this->value[-1], ['1', '3', '5', '7', '9'], true);
1,011✔
970
    }
971

972
    /**
973
     * Returns true if and only if the designated bit is set.
974
     *
975
     * Computes ((this & (1<<n)) != 0).
976
     *
977
     * @param int $n The bit to test, 0-based.
978
     *
979
     * @throws InvalidArgumentException If the bit to test is negative.
980
     *
981
     * @pure
982
     */
983
    public function testBit(int $n): bool
984
    {
985
        if ($n < 0) {
873✔
986
            throw new InvalidArgumentException('The bit to test cannot be negative.');
3✔
987
        }
988

989
        return $this->shiftedRight($n)->isOdd();
870✔
990
    }
991

992
    #[Override]
993
    public function compareTo(BigNumber|int|float|string $that): int
994
    {
995
        $that = BigNumber::of($that);
2,315✔
996

997
        if ($that instanceof BigInteger) {
2,315✔
998
            return CalculatorRegistry::get()->cmp($this->value, $that->value);
2,207✔
999
        }
1000

1001
        return -$that->compareTo($this);
108✔
1002
    }
1003

1004
    #[Override]
1005
    public function getSign(): int
1006
    {
1007
        return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
3,272✔
1008
    }
1009

1010
    #[Override]
1011
    public function toBigInteger(): BigInteger
1012
    {
1013
        return $this;
23,900✔
1014
    }
1015

1016
    #[Override]
1017
    public function toBigDecimal(): BigDecimal
1018
    {
1019
        return self::newBigDecimal($this->value);
5,277✔
1020
    }
1021

1022
    #[Override]
1023
    public function toBigRational(): BigRational
1024
    {
1025
        return self::newBigRational($this, BigInteger::one(), false, false);
474✔
1026
    }
1027

1028
    #[Override]
1029
    public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigDecimal
1030
    {
1031
        return $this->toBigDecimal()->toScale($scale, $roundingMode);
9✔
1032
    }
1033

1034
    #[Override]
1035
    public function toInt(): int
1036
    {
1037
        $intValue = filter_var($this->value, FILTER_VALIDATE_INT);
87✔
1038

1039
        if ($intValue === false) {
87✔
1040
            throw IntegerOverflowException::toIntOverflow($this);
15✔
1041
        }
1042

1043
        return $intValue;
72✔
1044
    }
1045

1046
    #[Override]
1047
    public function toFloat(): float
1048
    {
1049
        return (float) $this->value;
42✔
1050
    }
1051

1052
    /**
1053
     * Returns a string representation of this number in the given base.
1054
     *
1055
     * The output will always be lowercase for bases greater than 10.
1056
     *
1057
     * @throws InvalidArgumentException If the base is out of range.
1058
     *
1059
     * @pure
1060
     */
1061
    public function toBase(int $base): string
1062
    {
1063
        if ($base === 10) {
1,293✔
1064
            return $this->value;
12✔
1065
        }
1066

1067
        if ($base < 2 || $base > 36) {
1,281✔
1068
            throw new InvalidArgumentException(sprintf('Base %d is out of range [2, 36]', $base));
15✔
1069
        }
1070

1071
        return CalculatorRegistry::get()->toBase($this->value, $base);
1,266✔
1072
    }
1073

1074
    /**
1075
     * Returns a string representation of this number in an arbitrary base with a custom alphabet.
1076
     *
1077
     * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers;
1078
     * a NegativeNumberException will be thrown when attempting to call this method on a negative number.
1079
     *
1080
     * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8.
1081
     *
1082
     * @throws NegativeNumberException  If this number is negative.
1083
     * @throws InvalidArgumentException If the given alphabet does not contain at least 2 chars.
1084
     *
1085
     * @pure
1086
     */
1087
    public function toArbitraryBase(string $alphabet): string
1088
    {
1089
        $base = strlen($alphabet);
135✔
1090

1091
        if ($base < 2) {
135✔
1092
            throw new InvalidArgumentException('The alphabet must contain at least 2 chars.');
6✔
1093
        }
1094

1095
        if ($this->value[0] === '-') {
129✔
1096
            throw new NegativeNumberException(__FUNCTION__ . '() does not support negative numbers.');
3✔
1097
        }
1098

1099
        return CalculatorRegistry::get()->toArbitraryBase($this->value, $alphabet, $base);
126✔
1100
    }
1101

1102
    /**
1103
     * Returns a string of bytes containing the binary representation of this BigInteger.
1104
     *
1105
     * The string is in big-endian byte-order: the most significant byte is in the zeroth element.
1106
     *
1107
     * If `$signed` is true, the output will be in two's-complement representation, and a sign bit will be prepended to
1108
     * the output. If `$signed` is false, no sign bit will be prepended, and this method will throw an exception if the
1109
     * number is negative.
1110
     *
1111
     * The string will contain the minimum number of bytes required to represent this BigInteger, including a sign bit
1112
     * if `$signed` is true.
1113
     *
1114
     * This representation is compatible with the `fromBytes()` factory method, as long as the `$signed` flags match.
1115
     *
1116
     * @param bool $signed Whether to output a signed number in two's-complement representation with a leading sign bit.
1117
     *
1118
     * @throws NegativeNumberException If $signed is false, and the number is negative.
1119
     *
1120
     * @pure
1121
     */
1122
    public function toBytes(bool $signed = true): string
1123
    {
1124
        if (! $signed && $this->isNegative()) {
534✔
1125
            throw new NegativeNumberException('Cannot convert a negative number to a byte string when $signed is false.');
3✔
1126
        }
1127

1128
        $hex = $this->abs()->toBase(16);
531✔
1129

1130
        if (strlen($hex) % 2 !== 0) {
531✔
1131
            $hex = '0' . $hex;
219✔
1132
        }
1133

1134
        $baseHexLength = strlen($hex);
531✔
1135

1136
        if ($signed) {
531✔
1137
            if ($this->isNegative()) {
492✔
1138
                $bin = hex2bin($hex);
453✔
1139
                assert($bin !== false);
1140

1141
                $hex = bin2hex(~$bin);
453✔
1142
                $hex = self::fromBase($hex, 16)->plus(1)->toBase(16);
453✔
1143

1144
                $hexLength = strlen($hex);
453✔
1145

1146
                if ($hexLength < $baseHexLength) {
453✔
1147
                    $hex = str_repeat('0', $baseHexLength - $hexLength) . $hex;
72✔
1148
                }
1149

1150
                if ($hex[0] < '8') {
453✔
1151
                    $hex = 'FF' . $hex;
114✔
1152
                }
1153
            } else {
1154
                if ($hex[0] >= '8') {
39✔
1155
                    $hex = '00' . $hex;
21✔
1156
                }
1157
            }
1158
        }
1159

1160
        $result = hex2bin($hex);
531✔
1161
        assert($result !== false);
1162

1163
        return $result;
531✔
1164
    }
1165

1166
    /**
1167
     * @return numeric-string
1168
     */
1169
    #[Override]
1170
    public function toString(): string
1171
    {
1172
        /** @var numeric-string */
1173
        return $this->value;
23,070✔
1174
    }
1175

1176
    /**
1177
     * This method is required for serializing the object and SHOULD NOT be accessed directly.
1178
     *
1179
     * @internal
1180
     *
1181
     * @return array{value: string}
1182
     */
1183
    public function __serialize(): array
1184
    {
1185
        return ['value' => $this->value];
6✔
1186
    }
1187

1188
    /**
1189
     * This method is only here to allow unserializing the object and cannot be accessed directly.
1190
     *
1191
     * @internal
1192
     *
1193
     * @param array{value: string} $data
1194
     *
1195
     * @throws LogicException
1196
     */
1197
    public function __unserialize(array $data): void
1198
    {
1199
        /** @phpstan-ignore isset.initializedProperty */
1200
        if (isset($this->value)) {
9✔
1201
            throw new LogicException('__unserialize() is an internal function, it must not be called directly.');
3✔
1202
        }
1203

1204
        /** @phpstan-ignore deadCode.unreachable */
1205
        $this->value = $data['value'];
6✔
1206
    }
1207

1208
    #[Override]
1209
    protected static function from(BigNumber $number): static
1210
    {
1211
        return $number->toBigInteger();
24,020✔
1212
    }
1213
}
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