Subversion Repositories oidplus

Rev

Rev 291 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
208 daniel-mar 1
<?php
2
 
3
/*
4
 * PHP GMP-Supplement implemented using BCMath
5
 * Copyright 2020 Daniel Marschall, ViaThinkSoft
386 daniel-mar 6
 * Version 2020-09-12
208 daniel-mar 7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
 
21
if (function_exists('bcadd')) {
237 daniel-mar 22
        // ----------------- Implementation of GMP functions using BCMath -----------------
208 daniel-mar 23
 
24
        if (!function_exists('gmp_init') ) {
25
                define('GMP_ROUND_ZERO',     0);
26
                define('GMP_ROUND_PLUSINF',  1);
27
                define('GMP_ROUND_MINUSINF', 2);
28
                define('GMP_MSW_FIRST',      1);
29
                define('GMP_LSW_FIRST',      2);
30
                define('GMP_LITTLE_ENDIAN',  4);
31
                define('GMP_BIG_ENDIAN',     8);
32
                define('GMP_NATIVE_ENDIAN', 16);
33
                define('GMP_VERSION',  '6.0.0');
237 daniel-mar 34
 
208 daniel-mar 35
                // gmp_abs ( GMP $a ) : GMP
36
                // Absolute value
37
                function gmp_abs($a) {
38
                        bcscale(0);
39
                        if (bccomp($a, "0") == 1) {
40
                                return $a;
41
                        } else {
42
                                return bcmul($a, "-1");
43
                        }
44
                }
237 daniel-mar 45
 
208 daniel-mar 46
                // gmp_add ( GMP $a , GMP $b ) : GMP
47
                // Add numbers
48
                function gmp_add($a, $b) {
49
                        bcscale(0);
237 daniel-mar 50
 
208 daniel-mar 51
                        // bcadd ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
52
                        return bcadd($a, $b);
53
                }
237 daniel-mar 54
 
208 daniel-mar 55
                // gmp_and ( GMP $a , GMP $b ) : GMP
56
                // Bitwise AND
57
                function gmp_and($a, $b) {
58
                        bcscale(0);
59
                        // Convert $a and $b to a binary string
60
                        $ab = bc_dec2bin($a);
61
                        $bb = bc_dec2bin($b);
62
                        $length = max(strlen($ab), strlen($bb));
63
                        $ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
64
                        $bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
237 daniel-mar 65
 
208 daniel-mar 66
                        // Do the bitwise binary operation
67
                        $cb = '';
68
                        for ($i=0; $i<$length; $i++) {
69
                                $cb .= (($ab[$i] == 1) and ($bb[$i] == 1)) ? '1' : '0';
70
                        }
237 daniel-mar 71
 
208 daniel-mar 72
                        // Convert back to a decimal number
73
                        return bc_bin2dec($cb);
74
                }
237 daniel-mar 75
 
208 daniel-mar 76
                // gmp_binomial ( mixed $n , int $k ) : GMP
77
                // Calculates binomial coefficient
78
                function gmp_binomial($n, $k) {
79
                        bcscale(0);
212 daniel-mar 80
                        throw new Exception("gmp_binomial() NOT IMPLEMENTED");
208 daniel-mar 81
                }
237 daniel-mar 82
 
291 daniel-mar 83
                // gmp_clrbit ( GMP $a , int $index ) : void
208 daniel-mar 84
                // Clear bit
85
                function gmp_clrbit($a, $index) {
86
                        bcscale(0);
87
                        return gmp_setbit($a, $index, false);
88
                }
237 daniel-mar 89
 
208 daniel-mar 90
                // gmp_cmp ( GMP $a , GMP $b ) : int
91
                // Compare numbers
92
                function gmp_cmp($a, $b) {
93
                        bcscale(0);
237 daniel-mar 94
 
208 daniel-mar 95
                        // bccomp ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : int
96
                        return bccomp($a, $b);
97
                }
237 daniel-mar 98
 
208 daniel-mar 99
                // gmp_com ( GMP $a ) : GMP
100
                // Calculates one's complement
101
                function gmp_com($a) {
102
                        bcscale(0);
103
                        // Convert $a and $b to a binary string
104
                        $ab = bc_dec2bin($a);
237 daniel-mar 105
 
208 daniel-mar 106
                        // Swap every bit
107
                        for ($i=0; $i<strlen($ab); $i++) {
108
                                $ab[$i] = ($ab[$i] == '1' ? '0' : '1');
109
                        }
237 daniel-mar 110
 
208 daniel-mar 111
                        // Convert back to a decimal number
112
                        return bc_bin2dec($ab);
113
                }
237 daniel-mar 114
 
208 daniel-mar 115
                // gmp_div_q ( GMP $a , GMP $b [, int $round = GMP_ROUND_ZERO ] ) : GMP
116
                // Divide numbers
212 daniel-mar 117
                function gmp_div_q($a, $b, $round = GMP_ROUND_ZERO/*$round not implemented*/) {
208 daniel-mar 118
                        bcscale(0);
237 daniel-mar 119
 
208 daniel-mar 120
                        // bcdiv ( string $dividend , string $divisor [, int $scale = 0 ] ) : string
121
                        return bcdiv($a, $b);
122
                }
237 daniel-mar 123
 
208 daniel-mar 124
                // Divide numbers and get quotient and remainder
125
                // gmp_div_qr ( GMP $n , GMP $d [, int $round = GMP_ROUND_ZERO ] ) : array
212 daniel-mar 126
                function gmp_div_qr($n, $d, $round = GMP_ROUND_ZERO/*$round not implemented*/) {
208 daniel-mar 127
                        bcscale(0);
128
                        return array(
129
                                gmp_div_q($n, $d, $round),
130
                                gmp_div_r($n, $d, $round)
131
                        );
132
                }
237 daniel-mar 133
 
208 daniel-mar 134
                // Remainder of the division of numbers
135
                // gmp_div_r ( GMP $n , GMP $d [, int $round = GMP_ROUND_ZERO ] ) : GMP
212 daniel-mar 136
                function gmp_div_r($n, $d, $round = GMP_ROUND_ZERO/*$round not implemented*/) {
208 daniel-mar 137
                        bcscale(0);
138
                        // The remainder operator can be used with negative integers. The rule is:
139
                        // - Perform the operation as if both operands were positive.
140
                        // - If the left operand is negative, then make the result negative.
141
                        // - If the left operand is positive, then make the result positive.
142
                        // - Ignore the sign of the right operand in all cases.
143
                        $r = bcmod($n, $d);
144
                        if (bccomp($n, "0") < 0) $r = bcmul($r, "-1");
145
                        return $r;
146
                }
237 daniel-mar 147
 
208 daniel-mar 148
                // gmp_div ( GMP $a , GMP $b [, int $round = GMP_ROUND_ZERO ] ) : GMP
149
                // Divide numbers
212 daniel-mar 150
                function gmp_div($a, $b, $round = GMP_ROUND_ZERO/*$round not implemented*/) {
208 daniel-mar 151
                        bcscale(0);
152
                        return gmp_div_q($a, $b, $round); // Alias von gmp_div_q
153
                }
237 daniel-mar 154
 
208 daniel-mar 155
                // gmp_divexact ( GMP $n , GMP $d ) : GMP
156
                // Exact division of numbers
157
                function gmp_divexact($n, $d) {
158
                        bcscale(0);
386 daniel-mar 159
                        return bcdiv($n, $d);
208 daniel-mar 160
                }
237 daniel-mar 161
 
208 daniel-mar 162
                // gmp_export ( GMP $gmpnumber [, int $word_size = 1 [, int $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN ]] ) : string
163
                // Export to a binary string
164
                function gmp_export($gmpnumber, $word_size = 1, $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) {
237 daniel-mar 165
                        if ($word_size != 1) throw new Exception("Word size != 1 not implemented");
166
                        if ($options != GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) throw new Exception("Different options not implemented");
212 daniel-mar 167
 
208 daniel-mar 168
                        bcscale(0);
212 daniel-mar 169
                        $gmpnumber = bcmul($gmpnumber,"1"); // normalize
170
                        return gmp_init(bin2hex($gmpnumber), 16);
208 daniel-mar 171
                }
237 daniel-mar 172
 
208 daniel-mar 173
                // gmp_fact ( mixed $a ) : GMP
174
                // Factorial
175
                function gmp_fact($a) {
176
                        bcscale(0);
177
                        return bcfact($a);
178
                }
237 daniel-mar 179
 
208 daniel-mar 180
                // gmp_gcd ( GMP $a , GMP $b ) : GMP
181
                // Calculate GCD
182
                function gmp_gcd($a, $b) {
212 daniel-mar 183
                        bcscale(0);
210 daniel-mar 184
                        return gmp_gcdext($a, $b)['g'];
208 daniel-mar 185
                }
237 daniel-mar 186
 
208 daniel-mar 187
                // gmp_gcdext ( GMP $a , GMP $b ) : array
188
                // Calculate GCD and multipliers
189
                function gmp_gcdext($a, $b) {
190
                        bcscale(0);
210 daniel-mar 191
 
192
                        // Source: https://github.com/phpseclib/phpseclib/blob/master/phpseclib/Math/BigInteger/Engines/BCMath.php#L285
193
                        // modified to make it fit here and to be compatible with gmp_gcdext
237 daniel-mar 194
 
210 daniel-mar 195
                        $s = '1';
196
                        $t = '0';
197
                        $s_ = '0';
198
                        $t_ = '1';
237 daniel-mar 199
 
210 daniel-mar 200
                        while (bccomp($b, '0', 0) != 0) {
201
                                $q = bcdiv($a, $b, 0);
237 daniel-mar 202
 
210 daniel-mar 203
                                $temp = $a;
204
                                $a = $b;
205
                                $b = bcsub($temp, bcmul($b, $q, 0), 0);
237 daniel-mar 206
 
210 daniel-mar 207
                                $temp = $s;
208
                                $s = $s_;
209
                                $s_ = bcsub($temp, bcmul($s, $q, 0), 0);
237 daniel-mar 210
 
210 daniel-mar 211
                                $temp = $t;
212
                                $t = $t_;
213
                                $t_ = bcsub($temp, bcmul($t, $q, 0), 0);
214
                        }
237 daniel-mar 215
 
210 daniel-mar 216
                        return [
386 daniel-mar 217
                                'g' => /*$this->normalize*/($a),
218
                                's' => /*$this->normalize*/($s),
219
                                't' => /*$this->normalize*/($t)
210 daniel-mar 220
                        ];
208 daniel-mar 221
                }
237 daniel-mar 222
 
208 daniel-mar 223
                // gmp_hamdist ( GMP $a , GMP $b ) : int
224
                // Hamming distance
225
                function gmp_hamdist($a, $b) {
226
                        bcscale(0);
212 daniel-mar 227
                        throw new Exception("gmp_hamdist() NOT IMPLEMENTED");
208 daniel-mar 228
                }
237 daniel-mar 229
 
208 daniel-mar 230
                // gmp_import ( string $data [, int $word_size = 1 [, int $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN ]] ) : GMP
231
                // Import from a binary string
232
                function gmp_import($data, $word_size=1, $options=GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) {
233
                        bcscale(0);
212 daniel-mar 234
 
237 daniel-mar 235
                        if ($word_size != 1) throw new Exception("Word size != 1 not implemented");
236
                        if ($options != GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) throw new Exception("Different options not implemented");
212 daniel-mar 237
 
238
                        return gmp_init(hex2bin(gmp_strval(gmp_init($data), 16)));
208 daniel-mar 239
                }
237 daniel-mar 240
 
208 daniel-mar 241
                // gmp_init ( mixed $number [, int $base = 0 ] ) : GMP
242
                // Create GMP number
243
                function gmp_init($number, $base=0) {
244
                        bcscale(0);
245
                        if ($base == 0) {
246
                                // If base is 0 (default value), the actual base is determined from the leading characters:
247
                                // if the first two characters are 0x or 0X, hexadecimal is assumed,
248
                                // otherwise if the first character is "0", octal is assumed,
249
                                // otherwise decimal is assumed.
250
                                if (strtoupper(substr($number, 0, 2)) == '0X') {
251
                                        $base = 16;
252
                                } else if (strtoupper(substr($number, 0, 1)) == '0') {
253
                                        $base = 8;
254
                                } else {
255
                                        $base = 10;
256
                                }
257
                        }
237 daniel-mar 258
 
208 daniel-mar 259
                        if ($base == 10) {
260
                                return $number;
261
                        } else {
262
                                return base_convert_bigint($number, $base, 10);
263
                        }
264
                }
237 daniel-mar 265
 
208 daniel-mar 266
                // gmp_intval ( GMP $gmpnumber ) : int
267
                // Convert GMP number to integer
268
                function gmp_intval($gmpnumber) {
269
                        bcscale(0);
270
                        return (int)$gmpnumber;
271
                }
237 daniel-mar 272
 
208 daniel-mar 273
                // gmp_invert ( GMP $a , GMP $b ) : GMP
274
                // Inverse by modulo
275
                function gmp_invert($a, $b) {
276
                        bcscale(0);
237 daniel-mar 277
 
212 daniel-mar 278
                        // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L246
279
 
280
                        while (bccomp($a, 0)==-1) {
386 daniel-mar 281
                                $a=bcadd($b, $a);
212 daniel-mar 282
                        }
386 daniel-mar 283
                        while (bccomp($b, $a)==-1) {
284
                                $a=bcmod($a, $b);
212 daniel-mar 285
                        }
286
                        $c=$a;
386 daniel-mar 287
                        $d=$b;
212 daniel-mar 288
                        $uc=1;
289
                        $vc=0;
290
                        $ud=0;
291
                        $vd=1;
292
                        while (bccomp($c, 0)!=0) {
293
                                $temp1=$c;
294
                                $q=bcdiv($d, $c, 0);
295
                                $c=bcmod($d, $c);
296
                                $d=$temp1;
297
                                $temp2=$uc;
298
                                $temp3=$vc;
299
                                $uc=bcsub($ud, bcmul($q, $uc));
300
                                $vc=bcsub($vd, bcmul($q, $vc));
301
                                $ud=$temp2;
302
                                $vd=$temp3;
303
                        }
304
                        $result='';
305
                        if (bccomp($d, 1)==0) {
306
                                if (bccomp($ud, 0)==1) {
307
                                        $result=$ud;
308
                                } else {
386 daniel-mar 309
                                        $result=bcadd($ud, $b);
212 daniel-mar 310
                                }
311
                        } else {
386 daniel-mar 312
                                throw new ErrorException("ERROR: $a and $b are NOT relatively prime.");
212 daniel-mar 313
                        }
314
                        return $result;
208 daniel-mar 315
                }
237 daniel-mar 316
 
208 daniel-mar 317
                // gmp_jacobi ( GMP $a , GMP $p ) : int
318
                // Jacobi symbol
319
                function gmp_jacobi($a, $p) {
320
                        bcscale(0);
237 daniel-mar 321
 
212 daniel-mar 322
                        // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L136
323
 
386 daniel-mar 324
                        if ($p>=3 && $p%2==1) {
325
                                $a = bcmod($a, $p);
212 daniel-mar 326
                                if ($a == 0) return 0;
327
                                if ($a == 1) return 1;
328
                                $a1 = $a;
329
                                $e = 0;
330
                                while (bcmod($a1, 2) == 0) {
331
                                        $a1 = bcdiv($a1, 2);
332
                                        $e = bcadd($e, 1);
333
                                }
386 daniel-mar 334
                                $s = (bcmod($e, 2)==0 || bcmod($p, 8)==1 || bcmod($p, 8)==7) ? 1 : -1;
212 daniel-mar 335
                                if ($a1 == 1) return $s;
386 daniel-mar 336
                                if (bcmod($p, 4)==3 && bcmod($a1, 4)==3) $s = -$s;
337
                                return bcmul($s, gmp_jacobi(bcmod($p, $a1), $a1));
212 daniel-mar 338
                        } else {
339
                                return false;
340
                        }
208 daniel-mar 341
                }
237 daniel-mar 342
 
208 daniel-mar 343
                // gmp_kronecker ( mixed $a , mixed $b ) : int
344
                // Kronecker symbol
345
                function gmp_kronecker($a, $b) {
346
                        bcscale(0);
212 daniel-mar 347
                        throw new Exception("gmp_kronecker() NOT IMPLEMENTED");
208 daniel-mar 348
                }
237 daniel-mar 349
 
208 daniel-mar 350
                // gmp_lcm ( mixed $a , mixed $b ) : GMP
351
                // Calculate LCM
352
                function gmp_lcm($a, $b) {
353
                        bcscale(0);
237 daniel-mar 354
 
212 daniel-mar 355
                        if ((bccomp($a,'0')==0) && (bccomp($b,'0')==0)) {
356
                                return 0;
357
                        } else {
358
                                return gmp_div(gmp_abs(gmp_mul($a,$b)), gmp_gcd($a,$b));
359
                        }
208 daniel-mar 360
                }
237 daniel-mar 361
 
208 daniel-mar 362
                // gmp_legendre ( GMP $a , GMP $p ) : int
363
                // Legendre symbol
364
                function gmp_legendre($a, $p) {
365
                        bcscale(0);
212 daniel-mar 366
                        throw new Exception("gmp_legendre() NOT IMPLEMENTED");
208 daniel-mar 367
                }
237 daniel-mar 368
 
208 daniel-mar 369
                // gmp_mod ( GMP $n , GMP $d ) : GMP
370
                // Modulo operation
371
                function gmp_mod($n, $d) {
372
                        bcscale(0);
237 daniel-mar 373
 
208 daniel-mar 374
                        // bcmod ( string $dividend , string $divisor [, int $scale = 0 ] ) : string
375
                        return bcmod($n, $d);
376
                }
237 daniel-mar 377
 
208 daniel-mar 378
                // gmp_mul ( GMP $a , GMP $b ) : GMP
379
                // Multiply numbers
380
                function gmp_mul($a, $b) {
381
                        bcscale(0);
382
 
383
                        // bcmul ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
384
                        return bcmul($a, $b);
385
                }
237 daniel-mar 386
 
208 daniel-mar 387
                // gmp_neg ( GMP $a ) : GMP
388
                // Negate number
389
                function gmp_neg($a) {
390
                        bcscale(0);
391
                        return bcmul($a, "-1");
392
                }
237 daniel-mar 393
 
208 daniel-mar 394
                // gmp_nextprime ( int $a ) : GMP
395
                // Find next prime number
396
                function gmp_nextprime($a) {
397
                        bcscale(0);
237 daniel-mar 398
 
212 daniel-mar 399
                        // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L692
237 daniel-mar 400
 
386 daniel-mar 401
                        if (bccomp($a, 2) == -1) {
212 daniel-mar 402
                                return 2;
403
                        }
386 daniel-mar 404
                        $result = gmp_or(bcadd($a, 1), 1);
212 daniel-mar 405
                        while (!gmp_prob_prime($result)) {
406
                                $result = bcadd($result, 2);
407
                        }
237 daniel-mar 408
                        return $result;
208 daniel-mar 409
                }
237 daniel-mar 410
 
208 daniel-mar 411
                // gmp_or ( GMP $a , GMP $b ) : GMP
412
                // Bitwise OR
413
                function gmp_or($a, $b) {
414
                        bcscale(0);
415
                        // Convert $a and $b to a binary string
416
                        $ab = bc_dec2bin($a);
417
                        $bb = bc_dec2bin($b);
418
                        $length = max(strlen($ab), strlen($bb));
419
                        $ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
420
                        $bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
237 daniel-mar 421
 
208 daniel-mar 422
                        // Do the bitwise binary operation
423
                        $cb = '';
424
                        for ($i=0; $i<$length; $i++) {
425
                                $cb .= (($ab[$i] == 1) or ($bb[$i] == 1)) ? '1' : '0';
426
                        }
237 daniel-mar 427
 
208 daniel-mar 428
                        // Convert back to a decimal number
429
                        return bc_bin2dec($cb);
430
                }
237 daniel-mar 431
 
208 daniel-mar 432
                // gmp_perfect_power ( mixed $a ) : bool
433
                // Perfect power check
434
                function gmp_perfect_power($a) {
435
                        bcscale(0);
212 daniel-mar 436
                        throw new Exception("gmp_perfect_power() NOT IMPLEMENTED");
208 daniel-mar 437
                }
237 daniel-mar 438
 
208 daniel-mar 439
                // gmp_perfect_square ( GMP $a ) : bool
440
                // Perfect square check
441
                function gmp_perfect_square($a) {
442
                        bcscale(0);
212 daniel-mar 443
                        throw new Exception("gmp_perfect_square() NOT IMPLEMENTED");
208 daniel-mar 444
                }
237 daniel-mar 445
 
208 daniel-mar 446
                // gmp_popcount ( GMP $a ) : int
447
                // Population count
448
                function gmp_popcount($a) {
449
                        bcscale(0);
212 daniel-mar 450
                        $ab = bc_dec2bin($a);
451
                        return substr_count($ab, '1');
208 daniel-mar 452
                }
237 daniel-mar 453
 
208 daniel-mar 454
                // gmp_pow ( GMP $base , int $exp ) : GMP
455
                // Raise number into power
456
                function gmp_pow($base, $exp) {
457
                        bcscale(0);
237 daniel-mar 458
 
208 daniel-mar 459
                        // bcpow ( string $base , string $exponent [, int $scale = 0 ] ) : string
460
                        return bcpow($base, $exp);
461
                }
237 daniel-mar 462
 
208 daniel-mar 463
                // gmp_powm ( GMP $base , GMP $exp , GMP $mod ) : GMP
464
                // Raise number into power with modulo
465
                function gmp_powm($base, $exp, $mod) {
466
                        bcscale(0);
237 daniel-mar 467
 
208 daniel-mar 468
                        // bcpowmod ( string $base , string $exponent , string $modulus [, int $scale = 0 ] ) : string
469
                        return bcpowmod($base, $exp, $mod);
470
                }
237 daniel-mar 471
 
208 daniel-mar 472
                // gmp_prob_prime ( GMP $a [, int $reps = 10 ] ) : int
473
                // Check if number is "probably prime"
474
                function gmp_prob_prime($a, $reps=10) {
475
                        bcscale(0);
237 daniel-mar 476
 
212 daniel-mar 477
                        // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L655
478
 
479
                        $t = 40;
480
                        $k = 0;
386 daniel-mar 481
                        $m = bcsub($reps, 1);
212 daniel-mar 482
                        while (bcmod($m, 2) == 0) {
483
                                $k = bcadd($k, 1);
484
                                $m = bcdiv($m, 2);
485
                        }
486
                        for ($i=0; $i<$t; $i++) {
386 daniel-mar 487
                                $a = bcrand(1, bcsub($reps, 1));
212 daniel-mar 488
                                if ($m < 0) {
489
                                        return new ErrorException("Negative exponents ($m) not allowed");
490
                                } else {
386 daniel-mar 491
                                        $b0 = bcpowmod($a, $m, $reps);
237 daniel-mar 492
                                }
386 daniel-mar 493
                                if ($b0!=1 && $b0!=bcsub($reps, 1)) {
212 daniel-mar 494
                                        $j = 1;
386 daniel-mar 495
                                        while ($j<=$k-1 && $b0!=bcsub($reps, 1)) {
496
                                                $b0 = bcpowmod($b0, 2, $reps);
212 daniel-mar 497
                                                if ($b0 == 1) {
498
                                                        return false;
499
                                                }
500
                                                $j++;
501
                                        }
386 daniel-mar 502
                                        if ($b0 != bcsub($reps, 1)) {
212 daniel-mar 503
                                                return false;
504
                                        }
505
                                }
506
                        }
237 daniel-mar 507
                        return true;
208 daniel-mar 508
                }
237 daniel-mar 509
 
208 daniel-mar 510
                // gmp_random_bits ( int $bits ) : GMP
511
                // Random number
512
                function gmp_random_bits($bits) {
237 daniel-mar 513
                        bcscale(0);
212 daniel-mar 514
                        $min = 0;
515
                        $max = bcsub(bcpow('2', $bits), '1');
516
                        return bcrand($min, $max);
208 daniel-mar 517
                }
237 daniel-mar 518
 
208 daniel-mar 519
                // gmp_random_range ( GMP $min , GMP $max ) : GMP
520
                // Random number
521
                function gmp_random_range($min, $max) {
522
                        bcscale(0);
212 daniel-mar 523
                        return bcrand($min, $max);
208 daniel-mar 524
                }
237 daniel-mar 525
 
291 daniel-mar 526
                // gmp_random_seed ( mixed $seed ) : void
208 daniel-mar 527
                // Sets the RNG seed
528
                function gmp_random_seed($seed) {
529
                        bcscale(0);
212 daniel-mar 530
                        bcrand_seed($seed);
208 daniel-mar 531
                }
237 daniel-mar 532
 
208 daniel-mar 533
                // gmp_random ([ int $limiter = 20 ] ) : GMP
534
                // Random number (deprecated)
535
                function gmp_random($limiter=20) {
536
                        bcscale(0);
212 daniel-mar 537
                        throw new Exception("gmp_random() is deprecated! Please use gmp_random_bits() or gmp_random_range() instead.");
208 daniel-mar 538
                }
237 daniel-mar 539
 
208 daniel-mar 540
                // gmp_root ( GMP $a , int $nth ) : GMP
541
                // Take the integer part of nth root
542
                function gmp_root($a, $nth) {
543
                        bcscale(0);
212 daniel-mar 544
                        throw new Exception("gmp_root() NOT IMPLEMENTED");
208 daniel-mar 545
                }
237 daniel-mar 546
 
208 daniel-mar 547
                // gmp_rootrem ( GMP $a , int $nth ) : array
548
                // Take the integer part and remainder of nth root
549
                function gmp_rootrem($a, $nth) {
550
                        bcscale(0);
212 daniel-mar 551
                        throw new Exception("gmp_rootrem() NOT IMPLEMENTED");
208 daniel-mar 552
                }
237 daniel-mar 553
 
208 daniel-mar 554
                // gmp_scan0 ( GMP $a , int $start ) : int
555
                // Scan for 0
556
                function gmp_scan0($a, $start) {
557
                        bcscale(0);
558
 
559
                        $ab = bc_dec2bin($a);
237 daniel-mar 560
 
208 daniel-mar 561
                        if ($start < 0) throw new Exception("Starting index must be greater than or equal to zero");
562
                        if ($start >= strlen($ab)) return $start;
563
 
564
                        for ($i=$start; $i<strlen($ab); $i++) {
565
                                if ($ab[strlen($ab)-1-$i] == '0') {
566
                                        return $i;
567
                                }
568
                        }
237 daniel-mar 569
 
208 daniel-mar 570
                        return false;
571
                }
237 daniel-mar 572
 
208 daniel-mar 573
                // gmp_scan1 ( GMP $a , int $start ) : int
574
                // Scan for 1
575
                function gmp_scan1($a, $start) {
576
                        bcscale(0);
577
 
578
                        $ab = bc_dec2bin($a);
237 daniel-mar 579
 
208 daniel-mar 580
                        if ($start < 0) throw new Exception("Starting index must be greater than or equal to zero");
581
                        if ($start >= strlen($ab)) return -1;
582
 
583
                        for ($i=$start; $i<strlen($ab); $i++) {
584
                                if ($ab[strlen($ab)-1-$i] == '1') {
585
                                        return $i;
586
                                }
587
                        }
237 daniel-mar 588
 
208 daniel-mar 589
                        return false;
590
                }
237 daniel-mar 591
 
291 daniel-mar 592
                // gmp_setbit ( GMP $a , int $index [, bool $bit_on = TRUE ] ) : void
208 daniel-mar 593
                // Set bit
594
                function gmp_setbit($a, $index, $bit_on=TRUE) {
595
                        bcscale(0);
596
                        $ab = bc_dec2bin($a);
237 daniel-mar 597
 
598
                        if ($index < 0) throw new Exception("Invalid index");
208 daniel-mar 599
                        if ($index >= strlen($ab)) {
600
                                $ab = str_pad($ab, $index+1, '0', STR_PAD_LEFT);
601
                        }
237 daniel-mar 602
 
603
                        $ab[strlen($ab)-1-$index] = $bit_on ? '1' : '0';
604
 
208 daniel-mar 605
                        return bc_bin2dec($ab);
606
                }
237 daniel-mar 607
 
208 daniel-mar 608
                // gmp_sign ( GMP $a ) : int
609
                // Sign of number
610
                function gmp_sign($a) {
611
                        bcscale(0);
612
                        return bccomp($a, "0");
613
                }
237 daniel-mar 614
 
208 daniel-mar 615
                // gmp_sqrt ( GMP $a ) : GMP
616
                // Calculate square root
617
                function gmp_sqrt($a) {
618
                        bcscale(0);
237 daniel-mar 619
 
208 daniel-mar 620
                        // bcsqrt ( string $operand [, int $scale = 0 ] ) : string
621
                        return bcsqrt($a);
622
                }
237 daniel-mar 623
 
208 daniel-mar 624
                // gmp_sqrtrem ( GMP $a ) : array
625
                // Square root with remainder
626
                function gmp_sqrtrem($a) {
627
                        bcscale(0);
212 daniel-mar 628
                        throw new Exception("gmp_sqrtrem() NOT IMPLEMENTED");
208 daniel-mar 629
                }
237 daniel-mar 630
 
208 daniel-mar 631
                // gmp_strval ( GMP $gmpnumber [, int $base = 10 ] ) : string
632
                // Convert GMP number to string
633
                function gmp_strval($gmpnumber, $base=10) {
634
                        bcscale(0);
635
                        if ($base == 10) {
636
                                return $gmpnumber;
637
                        } else {
638
                                return base_convert_bigint($gmpnumber, 10, $base);
639
                        }
640
                }
237 daniel-mar 641
 
208 daniel-mar 642
                // gmp_sub ( GMP $a , GMP $b ) : GMP
643
                // Subtract numbers
644
                function gmp_sub($a, $b) {
645
                        bcscale(0);
237 daniel-mar 646
 
208 daniel-mar 647
                        // bcsub ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
648
                        return bcsub($a, $b);
649
                }
237 daniel-mar 650
 
208 daniel-mar 651
                // gmp_testbit ( GMP $a , int $index ) : bool
652
                // Tests if a bit is set
653
                function gmp_testbit($a, $index) {
654
                        bcscale(0);
655
                        $ab = bc_dec2bin($a);
237 daniel-mar 656
 
657
                        if ($index < 0) throw new Exception("Invalid index");
658
                        if ($index >= strlen($ab)) return ('0' == '1');
659
 
208 daniel-mar 660
                        return $ab[strlen($ab)-1-$index] == '1';
661
                }
237 daniel-mar 662
 
208 daniel-mar 663
                // gmp_xor ( GMP $a , GMP $b ) : GMP
664
                // Bitwise XOR
665
                function gmp_xor($a, $b) {
666
                        bcscale(0);
667
                        // Convert $a and $b to a binary string
668
                        $ab = bc_dec2bin($a);
669
                        $bb = bc_dec2bin($b);
670
                        $length = max(strlen($ab), strlen($bb));
671
                        $ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
672
                        $bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
237 daniel-mar 673
 
208 daniel-mar 674
                        // Do the bitwise binary operation
675
                        $cb = '';
676
                        for ($i=0; $i<$length; $i++) {
677
                                $cb .= (($ab[$i] == 1) xor ($bb[$i] == 1)) ? '1' : '0';
678
                        }
237 daniel-mar 679
 
208 daniel-mar 680
                        // Convert back to a decimal number
681
                        return bc_bin2dec($cb);
682
                }
683
        }
237 daniel-mar 684
 
208 daniel-mar 685
        // ----------------- Helper functions -----------------
237 daniel-mar 686
 
208 daniel-mar 687
        function base_convert_bigint($numstring, $frombase, $tobase) {
688
                $frombase_str = '';
689
                for ($i=0; $i<$frombase; $i++) {
690
                        $frombase_str .= strtoupper(base_convert($i, 10, 36));
691
                }
692
 
693
                $tobase_str = '';
694
                for ($i=0; $i<$tobase; $i++) {
695
                        $tobase_str .= strtoupper(base_convert($i, 10, 36));
696
                }
697
 
698
                $length = strlen($numstring);
699
                $result = '';
700
                $number = array();
701
                for ($i = 0; $i < $length; $i++) {
702
                        $number[$i] = stripos($frombase_str, $numstring[$i]);
703
                }
704
                do { // Loop until whole number is converted
705
                        $divide = 0;
706
                        $newlen = 0;
707
                        for ($i = 0; $i < $length; $i++) { // Perform division manually (which is why this works with big numbers)
708
                                $divide = $divide * $frombase + $number[$i];
709
                                if ($divide >= $tobase) {
710
                                        $number[$newlen++] = (int)($divide / $tobase);
711
                                        $divide = $divide % $tobase;
712
                                } else if ($newlen > 0) {
713
                                        $number[$newlen++] = 0;
714
                                }
715
                        }
716
                        $length = $newlen;
717
                        $result = $tobase_str[$divide] . $result; // Divide is basically $numstring % $tobase (i.e. the new character)
718
                }
719
                while ($newlen != 0);
720
 
721
                return $result;
722
        }
237 daniel-mar 723
 
208 daniel-mar 724
        function bc_dec2bin($decimal_i) {
725
                // https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/
237 daniel-mar 726
 
208 daniel-mar 727
                bcscale(0);
237 daniel-mar 728
 
208 daniel-mar 729
                $binary_i = '';
730
                do {
731
                        $binary_i = bcmod($decimal_i,'2') . $binary_i;
732
                        $decimal_i = bcdiv($decimal_i,'2');
733
                } while (bccomp($decimal_i,'0'));
237 daniel-mar 734
 
208 daniel-mar 735
                return $binary_i;
736
        }
237 daniel-mar 737
 
208 daniel-mar 738
        function bc_bin2dec($binary_i) {
739
                // https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/
237 daniel-mar 740
 
208 daniel-mar 741
                bcscale(0);
237 daniel-mar 742
 
208 daniel-mar 743
                $decimal_i = '0';
744
                for ($i = 0; $i < strlen($binary_i); $i++) {
745
                        $decimal_i = bcmul($decimal_i,'2');
746
                        $decimal_i = bcadd($decimal_i,$binary_i[$i]);
747
                }
237 daniel-mar 748
 
208 daniel-mar 749
                return $decimal_i;
750
        }
237 daniel-mar 751
 
752
        // ----------------- New functions -----------------
753
 
208 daniel-mar 754
        // Newly added: gmp_not / bcnot
755
        function bcnot($a) {
756
                bcscale(0);
386 daniel-mar 757
                // Convert $a to a binary string
208 daniel-mar 758
                $ab = bc_dec2bin($a);
386 daniel-mar 759
                $length = strlen($ab);
760
 
208 daniel-mar 761
                // Do the bitwise binary operation
762
                $cb = '';
763
                for ($i=0; $i<$length; $i++) {
386 daniel-mar 764
                        $cb .= ($ab[$i] == 1) ? '0' : '1';
208 daniel-mar 765
                }
237 daniel-mar 766
 
386 daniel-mar 767
                // Convert back to a decimal number
768
                return bc_bin2dec($cb);
208 daniel-mar 769
        }
770
        function gmp_not($a) {
212 daniel-mar 771
                bcscale(0);
208 daniel-mar 772
                return bcnot($a);
773
        }
237 daniel-mar 774
 
775
        // Newly added: bcshiftl / gmp_shiftl
208 daniel-mar 776
        function bcshiftl($num, $bits) {
777
                bcscale(0);
778
                return bcmul($num, bcpow('2', $bits));
779
        }
780
        function gmp_shiftl($num, $bits) {
212 daniel-mar 781
                bcscale(0);
208 daniel-mar 782
                return bcshiftl($num, $bits);
783
        }
237 daniel-mar 784
 
785
        // Newly added: bcshiftr / gmp_shiftr
208 daniel-mar 786
        function bcshiftr($num, $bits) {
787
                bcscale(0);
788
                return bcdiv($num, bcpow('2', $bits));
789
        }
790
        function gmp_shiftr($num, $bits) {
212 daniel-mar 791
                bcscale(0);
208 daniel-mar 792
                return bcshiftr($num, $bits);
793
        }
237 daniel-mar 794
 
208 daniel-mar 795
        // Newly added: bcfact (used by gmp_fact)
796
        function bcfact($a) {
797
                bcscale(0);
237 daniel-mar 798
 
208 daniel-mar 799
                // Source: https://www.php.net/manual/de/book.bc.php#116510
237 daniel-mar 800
 
208 daniel-mar 801
                if (!filter_var($a, FILTER_VALIDATE_INT) || $a <= 0) {
802
                        throw new InvalidArgumentException(sprintf('Argument must be natural number, "%s" given.', $a));
803
                }
237 daniel-mar 804
 
208 daniel-mar 805
                for ($result = '1'; $a > 0; $a--) {
806
                        $result = bcmul($result, $a);
807
                }
237 daniel-mar 808
 
208 daniel-mar 809
                return $result;
810
        }
212 daniel-mar 811
 
812
        // Newly added (used by gmp_prob_prime, gmp_random_range and gmp_random_bits)
813
        function bcrand($min, $max = false) {
814
                bcscale(0);
815
                // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/BCMathUtils.php#L7
816
                // Fixed: https://github.com/CityOfZion/neo-php/issues/16
817
                if (!$max) {
818
                        $max = $min;
819
                        $min = 0;
820
                }
821
                return bcadd(bcmul(bcdiv(mt_rand(), mt_getrandmax(), strlen($max)), bcsub(bcadd($max, 1), $min)), $min);
822
        }
237 daniel-mar 823
 
212 daniel-mar 824
        // Newly added (used by gmp_random_seed)
825
        function bcrand_seed($seed) {
826
                bcscale(0);
827
                mt_srand($seed);
828
        }
386 daniel-mar 829
}