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