PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 3.1.13.4
JetBackup – Backup, Restore & Migrate v3.1.13.4
3.1.22.4 3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / 3rdparty / phpseclib3 / Math / BigInteger / Engines / PHP.php
backup / src / JetBackup / 3rdparty / phpseclib3 / Math / BigInteger / Engines Last commit date
BCMath 9 months ago GMP 9 months ago PHP 9 months ago .htaccess 9 months ago BCMath.php 9 months ago Engine.php 9 months ago GMP.php 9 months ago OpenSSL.php 9 months ago PHP.php 9 months ago PHP32.php 9 months ago PHP64.php 9 months ago index.html 9 months ago web.config 9 months ago
PHP.php
1255 lines
1 <?php
2
3 /**
4 * Pure-PHP BigInteger Engine
5 *
6 * PHP version 5 and 7
7 *
8 * @author Jim Wigginton <terrafrost@php.net>
9 * @copyright 2017 Jim Wigginton
10 * @license http://www.opensource.org/licenses/mit-license.html MIT License
11 * @link http://pear.php.net/package/Math_BigInteger
12 */
13
14 declare(strict_types=1);
15
16 namespace phpseclib3\Math\BigInteger\Engines;
17
18 use phpseclib3\Common\Functions\Strings;
19 use phpseclib3\Exception\BadConfigurationException;
20 use phpseclib3\Exception\RuntimeException;
21
22 /**
23 * Pure-PHP Engine.
24 *
25 * @author Jim Wigginton <terrafrost@php.net>
26 */
27 abstract class PHP extends Engine
28 {
29 /**#@+
30 * Array constants
31 *
32 * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
33 * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
34 *
35 */
36 /**
37 * $result[self::VALUE] contains the value.
38 */
39 public const VALUE = 0;
40 /**
41 * $result[self::SIGN] contains the sign.
42 */
43 public const SIGN = 1;
44 /**#@-*/
45
46 /**
47 * Karatsuba Cutoff
48 *
49 * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
50 */
51 public const KARATSUBA_CUTOFF = 25;
52
53 /**
54 * Can Bitwise operations be done fast?
55 *
56 * @see parent::bitwise_leftRotate()
57 * @see parent::bitwise_rightRotate()
58 */
59 public const FAST_BITWISE = true;
60
61 /**
62 * Engine Directory
63 *
64 * @see parent::setModExpEngine
65 */
66 public const ENGINE_DIR = 'PHP';
67
68 /**
69 * Default constructor
70 *
71 * @param mixed $x integer Base-10 number or base-$base number if $base set.
72 * @return PHP
73 * @see parent::__construct()
74 */
75 public function __construct($x = 0, int $base = 10)
76 {
77 if (!isset(static::$isValidEngine[static::class])) {
78 static::$isValidEngine[static::class] = static::isValidEngine();
79 }
80 if (!static::$isValidEngine[static::class]) {
81 throw new BadConfigurationException(static::class . ' is not setup correctly on this system');
82 }
83
84 $this->value = [];
85 parent::__construct($x, $base);
86 }
87
88 /**
89 * Initialize a PHP BigInteger Engine instance
90 *
91 * @see parent::__construct()
92 */
93 protected function initialize(int $base): void
94 {
95 switch (abs($base)) {
96 case 16:
97 $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value;
98 $temp = new static(Strings::hex2bin($x), 256);
99 $this->value = $temp->value;
100 break;
101 case 10:
102 $temp = new static();
103
104 $multiplier = new static();
105 $multiplier->value = [static::MAX10];
106
107 $x = $this->value;
108
109 if ($x[0] == '-') {
110 $this->is_negative = true;
111 $x = substr($x, 1);
112 }
113
114 $x = str_pad(
115 $x,
116 strlen($x) + ((static::MAX10LEN - 1) * strlen($x)) % static::MAX10LEN,
117 '0',
118 STR_PAD_LEFT
119 );
120 while (strlen($x)) {
121 $temp = $temp->multiply($multiplier);
122 $temp = $temp->add(new static($this->int2bytes((int) substr($x, 0, static::MAX10LEN)), 256));
123 $x = substr($x, static::MAX10LEN);
124 }
125
126 $this->value = $temp->value;
127 }
128 }
129
130 /**
131 * Pads strings so that unpack may be used on them
132 */
133 protected function pad(string $str): string
134 {
135 $length = strlen($str);
136
137 $pad = 4 - (strlen($str) % 4);
138
139 return str_pad($str, $length + $pad, "\0", STR_PAD_LEFT);
140 }
141
142 /**
143 * Converts a BigInteger to a base-10 number.
144 */
145 public function toString(): string
146 {
147 if (!count($this->value)) {
148 return '0';
149 }
150
151 $temp = clone $this;
152 $temp->bitmask = false;
153 $temp->is_negative = false;
154
155 $divisor = new static();
156 $divisor->value = [static::MAX10];
157 $result = '';
158 while (count($temp->value)) {
159 [$temp, $mod] = $temp->divide($divisor);
160 $result = str_pad(
161 (string) $mod->value[0] ?? '',
162 static::MAX10LEN,
163 '0',
164 STR_PAD_LEFT
165 ) . $result;
166 }
167 $result = ltrim($result, '0');
168 if (empty($result)) {
169 $result = '0';
170 }
171
172 if ($this->is_negative) {
173 $result = '-' . $result;
174 }
175
176 return $result;
177 }
178
179 /**
180 * Converts a BigInteger to a byte string (eg. base-256).
181 */
182 public function toBytes(bool $twos_compliment = false): string
183 {
184 if ($twos_compliment) {
185 return $this->toBytesHelper();
186 }
187
188 if (!count($this->value)) {
189 return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
190 }
191
192 $result = $this->bitwise_small_split(8);
193 $result = implode('', array_map('chr', $result));
194
195 return $this->precision > 0 ?
196 str_pad(
197 substr($result, -(($this->precision + 7) >> 3)),
198 ($this->precision + 7) >> 3,
199 chr(0),
200 STR_PAD_LEFT
201 ) :
202 $result;
203 }
204
205 /**
206 * Performs addition.
207 */
208 protected static function addHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array
209 {
210 $x_size = count($x_value);
211 $y_size = count($y_value);
212
213 if ($x_size == 0) {
214 return [
215 self::VALUE => $y_value,
216 self::SIGN => $y_negative,
217 ];
218 } elseif ($y_size == 0) {
219 return [
220 self::VALUE => $x_value,
221 self::SIGN => $x_negative,
222 ];
223 }
224
225 // subtract, if appropriate
226 if ($x_negative != $y_negative) {
227 if ($x_value == $y_value) {
228 return [
229 self::VALUE => [],
230 self::SIGN => false,
231 ];
232 }
233
234 $temp = self::subtractHelper($x_value, false, $y_value, false);
235 $temp[self::SIGN] = self::compareHelper($x_value, false, $y_value, false) > 0 ?
236 $x_negative : $y_negative;
237
238 return $temp;
239 }
240
241 if ($x_size < $y_size) {
242 $size = $x_size;
243 $value = $y_value;
244 } else {
245 $size = $y_size;
246 $value = $x_value;
247 }
248
249 $value[count($value)] = 0; // just in case the carry adds an extra digit
250
251 $carry = 0;
252 for ($i = 0, $j = 1; $j < $size; $i += 2, $j += 2) {
253 //$sum = $x_value[$j] * static::BASE_FULL + $x_value[$i] + $y_value[$j] * static::BASE_FULL + $y_value[$i] + $carry;
254 $sum = ($x_value[$j] + $y_value[$j]) * static::BASE_FULL + $x_value[$i] + $y_value[$i] + $carry;
255 $carry = $sum >= static::MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
256 $sum = $carry ? $sum - static::MAX_DIGIT2 : $sum;
257
258 $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
259
260 $value[$i] = (int)($sum - static::BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
261 $value[$j] = $temp;
262 }
263
264 if ($j == $size) { // ie. if $y_size is odd
265 $sum = $x_value[$i] + $y_value[$i] + $carry;
266 $carry = $sum >= static::BASE_FULL;
267 $value[$i] = $carry ? $sum - static::BASE_FULL : $sum;
268 ++$i; // ie. let $i = $j since we've just done $value[$i]
269 }
270
271 if ($carry) {
272 for (; $value[$i] == static::MAX_DIGIT; ++$i) {
273 $value[$i] = 0;
274 }
275 ++$value[$i];
276 }
277
278 return [
279 self::VALUE => self::trim($value),
280 self::SIGN => $x_negative,
281 ];
282 }
283
284 /**
285 * Performs subtraction.
286 */
287 public static function subtractHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array
288 {
289 $x_size = count($x_value);
290 $y_size = count($y_value);
291
292 if ($x_size == 0) {
293 return [
294 self::VALUE => $y_value,
295 self::SIGN => !$y_negative,
296 ];
297 } elseif ($y_size == 0) {
298 return [
299 self::VALUE => $x_value,
300 self::SIGN => $x_negative,
301 ];
302 }
303
304 // add, if appropriate (ie. -$x - +$y or +$x - -$y)
305 if ($x_negative != $y_negative) {
306 $temp = self::addHelper($x_value, false, $y_value, false);
307 $temp[self::SIGN] = $x_negative;
308
309 return $temp;
310 }
311
312 $diff = self::compareHelper($x_value, $x_negative, $y_value, $y_negative);
313
314 if (!$diff) {
315 return [
316 self::VALUE => [],
317 self::SIGN => false,
318 ];
319 }
320
321 // switch $x and $y around, if appropriate.
322 if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
323 $temp = $x_value;
324 $x_value = $y_value;
325 $y_value = $temp;
326
327 $x_negative = !$x_negative;
328
329 $x_size = count($x_value);
330 $y_size = count($y_value);
331 }
332
333 // at this point, $x_value should be at least as big as - if not bigger than - $y_value
334
335 $carry = 0;
336 for ($i = 0, $j = 1; $j < $y_size; $i += 2, $j += 2) {
337 $sum = ($x_value[$j] - $y_value[$j]) * static::BASE_FULL + $x_value[$i] - $y_value[$i] - $carry;
338
339 $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
340 $sum = $carry ? $sum + static::MAX_DIGIT2 : $sum;
341
342 $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
343
344 $x_value[$i] = (int)($sum - static::BASE_FULL * $temp);
345 $x_value[$j] = $temp;
346 }
347
348 if ($j == $y_size) { // ie. if $y_size is odd
349 $sum = $x_value[$i] - $y_value[$i] - $carry;
350 $carry = $sum < 0;
351 $x_value[$i] = $carry ? $sum + static::BASE_FULL : $sum;
352 ++$i;
353 }
354
355 if ($carry) {
356 for (; !$x_value[$i]; ++$i) {
357 $x_value[$i] = static::MAX_DIGIT;
358 }
359 --$x_value[$i];
360 }
361
362 return [
363 self::VALUE => self::trim($x_value),
364 self::SIGN => $x_negative,
365 ];
366 }
367
368 /**
369 * Performs multiplication.
370 */
371 protected static function multiplyHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array
372 {
373 //if ( $x_value == $y_value ) {
374 // return [
375 // self::VALUE => self::square($x_value),
376 // self::SIGN => $x_sign != $y_value
377 // ];
378 //}
379
380 $x_length = count($x_value);
381 $y_length = count($y_value);
382
383 if (!$x_length || !$y_length) { // a 0 is being multiplied
384 return [
385 self::VALUE => [],
386 self::SIGN => false,
387 ];
388 }
389
390 return [
391 self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
392 self::trim(self::regularMultiply($x_value, $y_value)) :
393 self::trim(self::karatsuba($x_value, $y_value)),
394 self::SIGN => $x_negative != $y_negative,
395 ];
396 }
397
398 /**
399 * Performs Karatsuba multiplication on two BigIntegers
400 *
401 * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
402 * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
403 */
404 private static function karatsuba(array $x_value, array $y_value): array
405 {
406 $m = min(count($x_value) >> 1, count($y_value) >> 1);
407
408 if ($m < self::KARATSUBA_CUTOFF) {
409 return self::regularMultiply($x_value, $y_value);
410 }
411
412 $x1 = array_slice($x_value, $m);
413 $x0 = array_slice($x_value, 0, $m);
414 $y1 = array_slice($y_value, $m);
415 $y0 = array_slice($y_value, 0, $m);
416
417 $z2 = self::karatsuba($x1, $y1);
418 $z0 = self::karatsuba($x0, $y0);
419
420 $z1 = self::addHelper($x1, false, $x0, false);
421 $temp = self::addHelper($y1, false, $y0, false);
422 $z1 = self::karatsuba($z1[self::VALUE], $temp[self::VALUE]);
423 $temp = self::addHelper($z2, false, $z0, false);
424 $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);
425
426 $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
427 $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
428
429 $xy = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
430 $xy = self::addHelper($xy[self::VALUE], $xy[self::SIGN], $z0, false);
431
432 return $xy[self::VALUE];
433 }
434
435 /**
436 * Performs long multiplication on two BigIntegers
437 *
438 * Modeled after 'multiply' in MutableBigInteger.java.
439 */
440 protected static function regularMultiply(array $x_value, array $y_value): array
441 {
442 $x_length = count($x_value);
443 $y_length = count($y_value);
444
445 if (!$x_length || !$y_length) { // a 0 is being multiplied
446 return [];
447 }
448
449 $product_value = self::array_repeat(0, $x_length + $y_length);
450
451 // the following for loop could be removed if the for loop following it
452 // (the one with nested for loops) initially set $i to 0, but
453 // doing so would also make the result in one set of unnecessary adds,
454 // since on the outermost loops first pass, $product->value[$k] is going
455 // to always be 0
456
457 $carry = 0;
458 for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
459 $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
460 $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
461 $product_value[$j] = (int)($temp - static::BASE_FULL * $carry);
462 }
463
464 $product_value[$j] = $carry;
465
466 // the above for loop is what the previous comment was talking about. the
467 // following for loop is the "one with nested for loops"
468 for ($i = 1; $i < $y_length; ++$i) {
469 $carry = 0;
470
471 for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
472 $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
473 $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
474 $product_value[$k] = (int)($temp - static::BASE_FULL * $carry);
475 }
476
477 $product_value[$k] = $carry;
478 }
479
480 return $product_value;
481 }
482
483 /**
484 * Divides two BigIntegers.
485 *
486 * Returns an array whose first element contains the quotient and whose second element contains the
487 * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
488 * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
489 * and the divisor (basically, the "common residue" is the first positive modulo).
490 *
491 * @return array{static, static}
492 * @internal This function is based off of
493 * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
494 */
495 protected function divideHelper(PHP $y): array
496 {
497 if (count($y->value) == 1) {
498 [$q, $r] = $this->divide_digit($this->value, $y->value[0]);
499 $quotient = new static();
500 $remainder = new static();
501 $quotient->value = $q;
502 $remainder->value = [$r];
503 $quotient->is_negative = $this->is_negative != $y->is_negative;
504 return [$this->normalize($quotient), $this->normalize($remainder)];
505 }
506
507 $x = clone $this;
508 $y = clone $y;
509
510 $x_sign = $x->is_negative;
511 $y_sign = $y->is_negative;
512
513 $x->is_negative = $y->is_negative = false;
514
515 $diff = $x->compare($y);
516
517 if (!$diff) {
518 $temp = new static();
519 $temp->value = [1];
520 $temp->is_negative = $x_sign != $y_sign;
521 return [$this->normalize($temp), $this->normalize(static::$zero[static::class])];
522 }
523
524 if ($diff < 0) {
525 // if $x is negative, "add" $y.
526 if ($x_sign) {
527 $x = $y->subtract($x);
528 }
529 return [$this->normalize(static::$zero[static::class]), $this->normalize($x)];
530 }
531
532 // normalize $x and $y as described in HAC 14.23 / 14.24
533 $msb = $y->value[count($y->value) - 1];
534 for ($shift = 0; !($msb & static::MSB); ++$shift) {
535 $msb <<= 1;
536 }
537 $x->lshift($shift);
538 $y->lshift($shift);
539 $y_value = &$y->value;
540
541 $x_max = count($x->value) - 1;
542 $y_max = count($y->value) - 1;
543
544 $quotient = new static();
545 $quotient_value = &$quotient->value;
546 $quotient_value = self::array_repeat(0, $x_max - $y_max + 1);
547
548 static $temp, $lhs, $rhs;
549 if (!isset($temp)) {
550 $temp = new static();
551 $lhs = new static();
552 $rhs = new static();
553 }
554 if (static::class != $temp::class) {
555 $temp = new static();
556 $lhs = new static();
557 $rhs = new static();
558 }
559 $temp_value = &$temp->value;
560 $rhs_value = &$rhs->value;
561
562 // $temp = $y << ($x_max - $y_max-1) in base 2**26
563 $temp_value = array_merge(self::array_repeat(0, $x_max - $y_max), $y_value);
564
565 while ($x->compare($temp) >= 0) {
566 // calculate the "common residue"
567 ++$quotient_value[$x_max - $y_max];
568 $x = $x->subtract($temp);
569 $x_max = count($x->value) - 1;
570 }
571
572 for ($i = $x_max; $i >= $y_max + 1; --$i) {
573 $x_value = &$x->value;
574 $x_window = [
575 $x_value[$i] ?? 0,
576 $x_value[$i - 1] ?? 0,
577 $x_value[$i - 2] ?? 0,
578 ];
579 $y_window = [
580 $y_value[$y_max],
581 ($y_max > 0) ? $y_value[$y_max - 1] : 0,
582 ];
583
584 $q_index = $i - $y_max - 1;
585 if ($x_window[0] == $y_window[0]) {
586 $quotient_value[$q_index] = static::MAX_DIGIT;
587 } else {
588 $quotient_value[$q_index] = self::safe_divide(
589 $x_window[0] * static::BASE_FULL + $x_window[1],
590 $y_window[0]
591 );
592 }
593
594 $temp_value = [$y_window[1], $y_window[0]];
595
596 $lhs->value = [$quotient_value[$q_index]];
597 $lhs = $lhs->multiply($temp);
598
599 $rhs_value = [$x_window[2], $x_window[1], $x_window[0]];
600
601 while ($lhs->compare($rhs) > 0) {
602 --$quotient_value[$q_index];
603
604 $lhs->value = [$quotient_value[$q_index]];
605 $lhs = $lhs->multiply($temp);
606 }
607
608 $adjust = self::array_repeat(0, $q_index);
609 $temp_value = [$quotient_value[$q_index]];
610 $temp = $temp->multiply($y);
611 $temp_value = &$temp->value;
612 if (count($temp_value)) {
613 $temp_value = array_merge($adjust, $temp_value);
614 }
615
616 $x = $x->subtract($temp);
617
618 if ($x->compare(static::$zero[static::class]) < 0) {
619 $temp_value = array_merge($adjust, $y_value);
620 $x = $x->add($temp);
621
622 --$quotient_value[$q_index];
623 }
624
625 $x_max = count($x_value) - 1;
626 }
627
628 // unnormalize the remainder
629 $x->rshift($shift);
630
631 $quotient->is_negative = $x_sign != $y_sign;
632
633 // calculate the "common residue", if appropriate
634 if ($x_sign) {
635 $y->rshift($shift);
636 $x = $y->subtract($x);
637 }
638
639 return [$this->normalize($quotient), $this->normalize($x)];
640 }
641
642 /**
643 * Divides a BigInteger by a regular integer
644 *
645 * abc / x = a00 / x + b0 / x + c / x
646 */
647 private static function divide_digit(array $dividend, int $divisor): array
648 {
649 $carry = 0;
650 $result = [];
651
652 for ($i = count($dividend) - 1; $i >= 0; --$i) {
653 $temp = static::BASE_FULL * $carry + (int) $dividend[$i];
654 $result[$i] = self::safe_divide($temp, $divisor);
655 $carry = (int)($temp - $divisor * $result[$i]);
656 }
657
658 return [$result, $carry];
659 }
660
661 /**
662 * Single digit division
663 *
664 * Even if int64 is being used the division operator will return a float64 value
665 * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
666 * have the precision of int64 this is a problem so, when int64 is being used,
667 * we'll guarantee that the dividend is divisible by first subtracting the remainder.
668 */
669 private static function safe_divide(int $x, int $y): int
670 {
671 if (static::BASE === 26) {
672 return (int)($x / $y);
673 }
674
675 // static::BASE === 31
676 /** @var int */
677 return ($x - ($x % $y)) / $y;
678 }
679
680 /**
681 * Convert an array / boolean to a PHP BigInteger object
682 *
683 * @return static
684 */
685 protected function convertToObj(array $arr): PHP
686 {
687 $result = new static();
688 $result->value = $arr[self::VALUE];
689 $result->is_negative = $arr[self::SIGN];
690
691 return $this->normalize($result);
692 }
693
694 /**
695 * Normalize
696 *
697 * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
698 *
699 * @return static
700 */
701 protected function normalize(PHP $result): PHP
702 {
703 $result->precision = $this->precision;
704 $result->bitmask = $this->bitmask;
705
706 $value = &$result->value;
707
708 if (!count($value)) {
709 $result->is_negative = false;
710 return $result;
711 }
712
713 $value = static::trim($value);
714
715 if (!empty($result->bitmask->value)) {
716 $length = min(count($value), count($result->bitmask->value));
717 $value = array_slice($value, 0, $length);
718
719 for ($i = 0; $i < $length; ++$i) {
720 $value[$i] = $value[$i] & $result->bitmask->value[$i];
721 }
722
723 $value = static::trim($value);
724 }
725
726 return $result;
727 }
728
729 /**
730 * Compares two numbers.
731 *
732 * @see static::compare()
733 */
734 protected static function compareHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): int
735 {
736 if ($x_negative != $y_negative) {
737 return (!$x_negative && $y_negative) ? 1 : -1;
738 }
739
740 $result = $x_negative ? -1 : 1;
741
742 if (count($x_value) != count($y_value)) {
743 return (count($x_value) > count($y_value)) ? $result : -$result;
744 }
745 $size = max(count($x_value), count($y_value));
746
747 $x_value = array_pad($x_value, $size, 0);
748 $y_value = array_pad($y_value, $size, 0);
749
750 for ($i = count($x_value) - 1; $i >= 0; --$i) {
751 if ($x_value[$i] != $y_value[$i]) {
752 return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
753 }
754 }
755
756 return 0;
757 }
758
759 /**
760 * Absolute value.
761 */
762 public function abs(): PHP
763 {
764 $temp = new static();
765 $temp->value = $this->value;
766
767 return $temp;
768 }
769
770 /**
771 * Trim
772 *
773 * Removes leading zeros
774 *
775 * @param list<static> $value
776 * @return list<static>
777 */
778 protected static function trim(array $value): array
779 {
780 for ($i = count($value) - 1; $i >= 0; --$i) {
781 if ($value[$i]) {
782 break;
783 }
784 unset($value[$i]);
785 }
786
787 return $value;
788 }
789
790 /**
791 * Logical Right Shift
792 *
793 * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
794 */
795 public function bitwise_rightShift(int $shift): PHP
796 {
797 $temp = new static();
798
799 // could just replace lshift with this, but then all lshift() calls would need to be rewritten
800 // and I don't want to do that...
801 $temp->value = $this->value;
802 $temp->rshift($shift);
803
804 return $this->normalize($temp);
805 }
806
807 /**
808 * Logical Left Shift
809 *
810 * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
811 */
812 public function bitwise_leftShift(int $shift): PHP
813 {
814 $temp = new static();
815 // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
816 // and I don't want to do that...
817 $temp->value = $this->value;
818 $temp->lshift($shift);
819
820 return $this->normalize($temp);
821 }
822
823 /**
824 * Converts 32-bit integers to bytes.
825 */
826 private static function int2bytes(int $x): string
827 {
828 return ltrim(pack('N', $x), chr(0));
829 }
830
831 /**
832 * Array Repeat
833 */
834 protected static function array_repeat(int $input, int $multiplier): array
835 {
836 return $multiplier ? array_fill(0, $multiplier, $input) : [];
837 }
838
839 /**
840 * Logical Left Shift
841 *
842 * Shifts BigInteger's by $shift bits.
843 */
844 protected function lshift(int $shift): void
845 {
846 if ($shift == 0) {
847 return;
848 }
849
850 $num_digits = (int)($shift / static::BASE);
851 $shift %= static::BASE;
852 $shift = 1 << $shift;
853
854 $carry = 0;
855
856 for ($i = 0; $i < count($this->value); ++$i) {
857 $temp = $this->value[$i] * $shift + $carry;
858 $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
859 $this->value[$i] = (int)($temp - $carry * static::BASE_FULL);
860 }
861
862 if ($carry) {
863 $this->value[count($this->value)] = $carry;
864 }
865
866 while ($num_digits--) {
867 array_unshift($this->value, 0);
868 }
869 }
870
871 /**
872 * Logical Right Shift
873 *
874 * Shifts BigInteger's by $shift bits.
875 */
876 protected function rshift(int $shift): void
877 {
878 if ($shift == 0) {
879 return;
880 }
881
882 $num_digits = (int)($shift / static::BASE);
883 $shift %= static::BASE;
884 $carry_shift = static::BASE - $shift;
885 $carry_mask = (1 << $shift) - 1;
886
887 if ($num_digits) {
888 $this->value = array_slice($this->value, $num_digits);
889 }
890
891 $carry = 0;
892
893 for ($i = count($this->value) - 1; $i >= 0; --$i) {
894 $temp = $this->value[$i] >> $shift | $carry;
895 $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
896 $this->value[$i] = $temp;
897 }
898
899 $this->value = static::trim($this->value);
900 }
901
902 /**
903 * Performs modular exponentiation.
904 */
905 protected function powModInner(PHP $e, PHP $n): PHP
906 {
907 try {
908 $class = static::$modexpEngine[static::class];
909 return $class::powModHelper($this, $e, $n, static::class);
910 } catch (\Exception $err) {
911 return PHP\DefaultEngine::powModHelper($this, $e, $n, static::class);
912 }
913 }
914
915 /**
916 * Performs squaring
917 *
918 * @param list<static> $x
919 * @return list<static>
920 */
921 protected static function square(array $x): array
922 {
923 return count($x) < 2 * self::KARATSUBA_CUTOFF ?
924 self::trim(self::baseSquare($x)) :
925 self::trim(self::karatsubaSquare($x));
926 }
927
928 /**
929 * Performs traditional squaring on two BigIntegers
930 *
931 * Squaring can be done faster than multiplying a number by itself can be. See
932 * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
933 * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
934 */
935 protected static function baseSquare(array $value): array
936 {
937 if (empty($value)) {
938 return [];
939 }
940 $square_value = self::array_repeat(0, 2 * count($value));
941
942 for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
943 $i2 = $i << 1;
944
945 $temp = $square_value[$i2] + $value[$i] * $value[$i];
946 $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
947 $square_value[$i2] = (int)($temp - static::BASE_FULL * $carry);
948
949 // note how we start from $i+1 instead of 0 as we do in multiplication.
950 for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
951 $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
952 $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
953 $square_value[$k] = (int)($temp - static::BASE_FULL * $carry);
954 }
955
956 // the following line can yield values larger 2**15. at this point, PHP should switch
957 // over to floats.
958 $square_value[$i + $max_index + 1] = $carry;
959 }
960
961 return $square_value;
962 }
963
964 /**
965 * Performs Karatsuba "squaring" on two BigIntegers
966 *
967 * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
968 * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
969 */
970 protected static function karatsubaSquare(array $value): array
971 {
972 $m = count($value) >> 1;
973
974 if ($m < self::KARATSUBA_CUTOFF) {
975 return self::baseSquare($value);
976 }
977
978 $x1 = array_slice($value, $m);
979 $x0 = array_slice($value, 0, $m);
980
981 $z2 = self::karatsubaSquare($x1);
982 $z0 = self::karatsubaSquare($x0);
983
984 $z1 = self::addHelper($x1, false, $x0, false);
985 $z1 = self::karatsubaSquare($z1[self::VALUE]);
986 $temp = self::addHelper($z2, false, $z0, false);
987 $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);
988
989 $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
990 $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
991
992 $xx = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
993 $xx = self::addHelper($xx[self::VALUE], $xx[self::SIGN], $z0, false);
994
995 return $xx[self::VALUE];
996 }
997
998 /**
999 * Make the current number odd
1000 *
1001 * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
1002 *
1003 * @see self::randomPrime()
1004 */
1005 protected function make_odd(): void
1006 {
1007 $this->value[0] |= 1;
1008 }
1009
1010 /**
1011 * Test the number against small primes.
1012 *
1013 * @see self::isPrime()
1014 */
1015 protected function testSmallPrimes(): bool
1016 {
1017 if ($this->value == [1]) {
1018 return false;
1019 }
1020 if ($this->value == [2]) {
1021 return true;
1022 }
1023 if (~$this->value[0] & 1) {
1024 return false;
1025 }
1026
1027 $value = $this->value;
1028 foreach (static::PRIMES as $prime) {
1029 [, $r] = self::divide_digit($value, $prime);
1030 if (!$r) {
1031 return count($value) == 1 && $value[0] == $prime;
1032 }
1033 }
1034
1035 return true;
1036 }
1037
1038 /**
1039 * Scan for 1 and right shift by that amount
1040 *
1041 * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
1042 *
1043 * @return int
1044 * @see self::isPrime()
1045 */
1046 public static function scan1divide(PHP $r)
1047 {
1048 $r_value = &$r->value;
1049 for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
1050 $temp = ~$r_value[$i] & static::MAX_DIGIT;
1051 for ($j = 1; ($temp >> $j) & 1; ++$j) {
1052 }
1053 if ($j <= static::BASE) {
1054 break;
1055 }
1056 }
1057 $s = static::BASE * $i + $j;
1058 $r->rshift($s);
1059 return $s;
1060 }
1061
1062 /**
1063 * Performs exponentiation.
1064 */
1065 protected function powHelper(PHP $n): PHP
1066 {
1067 if ($n->compare(static::$zero[static::class]) == 0) {
1068 return new static(1);
1069 } // n^0 = 1
1070
1071 $temp = clone $this;
1072 while (!$n->equals(static::$one[static::class])) {
1073 $temp = $temp->multiply($this);
1074 $n = $n->subtract(static::$one[static::class]);
1075 }
1076
1077 return $temp;
1078 }
1079
1080 /**
1081 * Is Odd?
1082 */
1083 public function isOdd(): bool
1084 {
1085 return (bool)($this->value[0] & 1);
1086 }
1087
1088 /**
1089 * Tests if a bit is set
1090 */
1091 public function testBit($x): bool
1092 {
1093 $digit = (int) floor($x / static::BASE);
1094 $bit = $x % static::BASE;
1095
1096 if (!isset($this->value[$digit])) {
1097 return false;
1098 }
1099
1100 return (bool)($this->value[$digit] & (1 << $bit));
1101 }
1102
1103 /**
1104 * Is Negative?
1105 */
1106 public function isNegative(): bool
1107 {
1108 return $this->is_negative;
1109 }
1110
1111 /**
1112 * Negate
1113 *
1114 * Given $k, returns -$k
1115 *
1116 * @return static
1117 */
1118 public function negate(): PHP
1119 {
1120 $temp = clone $this;
1121 $temp->is_negative = !$temp->is_negative;
1122
1123 return $temp;
1124 }
1125
1126 /**
1127 * Bitwise Split
1128 *
1129 * Splits BigInteger's into chunks of $split bits
1130 *
1131 * @return list<static>
1132 */
1133 public function bitwise_split(int $split): array
1134 {
1135 if ($split < 1) {
1136 throw new RuntimeException('Offset must be greater than 1');
1137 }
1138
1139 $width = (int)($split / static::BASE);
1140 if (!$width) {
1141 $arr = $this->bitwise_small_split($split);
1142 return array_map(function ($digit) {
1143 $temp = new static();
1144 $temp->value = $digit != 0 ? [$digit] : [];
1145 return $temp;
1146 }, $arr);
1147 }
1148
1149 $vals = [];
1150 $val = $this->value;
1151
1152 $i = $overflow = 0;
1153 $len = count($val);
1154 while ($i < $len) {
1155 $digit = [];
1156 if (!$overflow) {
1157 $digit = array_slice($val, $i, $width);
1158 $i += $width;
1159 $overflow = $split % static::BASE;
1160 if ($overflow) {
1161 $mask = (1 << $overflow) - 1;
1162 $temp = $val[$i] ?? 0;
1163 $digit[] = $temp & $mask;
1164 }
1165 } else {
1166 $remaining = static::BASE - $overflow;
1167 $tempsplit = $split - $remaining;
1168 $tempwidth = (int)($tempsplit / static::BASE + 1);
1169 $digit = array_slice($val, $i, $tempwidth);
1170 $i += $tempwidth;
1171 $tempoverflow = $tempsplit % static::BASE;
1172 if ($tempoverflow) {
1173 $tempmask = (1 << $tempoverflow) - 1;
1174 $temp = $val[$i] ?? 0;
1175 $digit[] = $temp & $tempmask;
1176 }
1177 $newbits = 0;
1178 for ($j = count($digit) - 1; $j >= 0; $j--) {
1179 $temp = $digit[$j] & $mask;
1180 $digit[$j] = ($digit[$j] >> $overflow) | ($newbits << $remaining);
1181 $newbits = $temp;
1182 }
1183 $overflow = $tempoverflow;
1184 $mask = $tempmask;
1185 }
1186 $temp = new static();
1187 $temp->value = static::trim($digit);
1188 $vals[] = $temp;
1189 }
1190
1191 return array_reverse($vals);
1192 }
1193
1194 /**
1195 * Bitwise Split where $split < static::BASE
1196 *
1197 * @return list<int>
1198 */
1199 private function bitwise_small_split(int $split): array
1200 {
1201 $vals = [];
1202 $val = $this->value;
1203
1204 $mask = (1 << $split) - 1;
1205
1206 $i = $overflow = 0;
1207 $len = count($val);
1208 $val[] = 0;
1209 $remaining = static::BASE;
1210 while ($i != $len) {
1211 $digit = $val[$i] & $mask;
1212 $val[$i] >>= $split;
1213 if (!$overflow) {
1214 $remaining -= $split;
1215 $overflow = $split <= $remaining ? 0 : $split - $remaining;
1216
1217 if (!$remaining) {
1218 $i++;
1219 $remaining = static::BASE;
1220 $overflow = 0;
1221 }
1222 } elseif (++$i != $len) {
1223 $tempmask = (1 << $overflow) - 1;
1224 $digit |= ($val[$i] & $tempmask) << $remaining;
1225 $val[$i] >>= $overflow;
1226 $remaining = static::BASE - $overflow;
1227 $overflow = $split <= $remaining ? 0 : $split - $remaining;
1228 }
1229
1230 $vals[] = $digit;
1231 }
1232
1233 while ($vals[count($vals) - 1] == 0) {
1234 unset($vals[count($vals) - 1]);
1235 }
1236
1237 return array_reverse($vals);
1238 }
1239
1240 /**
1241 * @return bool
1242 */
1243 protected static function testJITOnWindows()
1244 {
1245 // see https://github.com/php/php-src/issues/11917
1246 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && function_exists('opcache_get_status') && PHP_VERSION_ID < 80213 && !defined('PHPSECLIB_ALLOW_JIT')) {
1247 $status = opcache_get_status();
1248 if ($status && isset($status['jit']) && $status['jit']['enabled'] && $status['jit']['on']) {
1249 return true;
1250 }
1251 }
1252 return false;
1253 }
1254 }
1255