Subversion Repositories oidplus

Rev

Rev 208 | 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
6
 * Version 2020-02-27
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')) {
22
        // ----------------- Implementation of GMP functions using BCMath ----------------- 
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');
34
 
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
                }
45
 
46
                // gmp_add ( GMP $a , GMP $b ) : GMP
47
                // Add numbers
48
                function gmp_add($a, $b) {
49
                        bcscale(0);
50
 
51
                        // bcadd ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
52
                        return bcadd($a, $b);
53
                }
54
 
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);
65
 
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
                        }
71
 
72
                        // Convert back to a decimal number
73
                        return bc_bin2dec($cb);
74
                }
75
 
76
                // gmp_binomial ( mixed $n , int $k ) : GMP
77
                // Calculates binomial coefficient
78
                function gmp_binomial($n, $k) {
79
                        bcscale(0);
80
                        throw new Exception("NOT IMPLEMENTED");
81
                }
82
 
83
                // gmp_clrbit ( GMP $a , int $index ) : void
84
                // Clear bit
85
                function gmp_clrbit($a, $index) {
86
                        bcscale(0);
87
                        return gmp_setbit($a, $index, false);
88
                }
89
 
90
                // gmp_cmp ( GMP $a , GMP $b ) : int
91
                // Compare numbers
92
                function gmp_cmp($a, $b) {
93
                        bcscale(0);
94
 
95
                        // bccomp ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : int
96
                        return bccomp($a, $b);
97
                }
98
 
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);
105
 
106
                        // Swap every bit
107
                        for ($i=0; $i<strlen($ab); $i++) {
108
                                $ab[$i] = ($ab[$i] == '1' ? '0' : '1');
109
                        }
110
 
111
                        // Convert back to a decimal number
112
                        return bc_bin2dec($ab);
113
                }
114
 
115
                // gmp_div_q ( GMP $a , GMP $b [, int $round = GMP_ROUND_ZERO ] ) : GMP
116
                // Divide numbers
117
                function gmp_div_q($a, $b, $round = GMP_ROUND_ZERO/*not implemented*/) {
118
                        bcscale(0);
119
 
120
                        // bcdiv ( string $dividend , string $divisor [, int $scale = 0 ] ) : string
121
                        return bcdiv($a, $b);
122
                }
123
 
124
                // Divide numbers and get quotient and remainder
125
                // gmp_div_qr ( GMP $n , GMP $d [, int $round = GMP_ROUND_ZERO ] ) : array
126
                function gmp_div_qr($n, $d, $round = GMP_ROUND_ZERO/*not implemented*/) {
127
                        bcscale(0);
128
                        return array(
129
                                gmp_div_q($n, $d, $round),
130
                                gmp_div_r($n, $d, $round)
131
                        );
132
                }
133
 
134
                // Remainder of the division of numbers
135
                // gmp_div_r ( GMP $n , GMP $d [, int $round = GMP_ROUND_ZERO ] ) : GMP
136
                function gmp_div_r($n, $d, $round = GMP_ROUND_ZERO/*not implemented*/) {
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
                }
147
 
148
                // gmp_div ( GMP $a , GMP $b [, int $round = GMP_ROUND_ZERO ] ) : GMP
149
                // Divide numbers
150
                function gmp_div($a, $b, $round = GMP_ROUND_ZERO/*not implemented*/) {
151
                        bcscale(0);
152
                        return gmp_div_q($a, $b, $round); // Alias von gmp_div_q
153
                }
154
 
155
                // gmp_divexact ( GMP $n , GMP $d ) : GMP
156
                // Exact division of numbers
157
                function gmp_divexact($n, $d) {
158
                        bcscale(0);
159
                        return bcdiv($a, $b);
160
                }
161
 
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) {
165
                        bcscale(0);
166
                        throw new Exception("NOT IMPLEMENTED");
167
                }
168
 
169
                // gmp_fact ( mixed $a ) : GMP
170
                // Factorial
171
                function gmp_fact($a) {
172
                        bcscale(0);
173
                        return bcfact($a);
174
                }
175
 
176
                // gmp_gcd ( GMP $a , GMP $b ) : GMP
177
                // Calculate GCD
178
                function gmp_gcd($a, $b) {
210 daniel-mar 179
                        return gmp_gcdext($a, $b)['g'];
208 daniel-mar 180
                }
181
 
182
                // gmp_gcdext ( GMP $a , GMP $b ) : array
183
                // Calculate GCD and multipliers
184
                function gmp_gcdext($a, $b) {
185
                        bcscale(0);
210 daniel-mar 186
 
187
                        // Source: https://github.com/phpseclib/phpseclib/blob/master/phpseclib/Math/BigInteger/Engines/BCMath.php#L285
188
                        // modified to make it fit here and to be compatible with gmp_gcdext
189
 
190
                        $s = '1';
191
                        $t = '0';
192
                        $s_ = '0';
193
                        $t_ = '1';
194
 
195
                        while (bccomp($b, '0', 0) != 0) {
196
                                $q = bcdiv($a, $b, 0);
197
 
198
                                $temp = $a;
199
                                $a = $b;
200
                                $b = bcsub($temp, bcmul($b, $q, 0), 0);
201
 
202
                                $temp = $s;
203
                                $s = $s_;
204
                                $s_ = bcsub($temp, bcmul($s, $q, 0), 0);
205
 
206
                                $temp = $t;
207
                                $t = $t_;
208
                                $t_ = bcsub($temp, bcmul($t, $q, 0), 0);
209
                        }
210
 
211
                        return [
212
                                'g' => $this->normalize($a),
213
                                's' => $this->normalize($s),
214
                                't' => $this->normalize($t)
215
                        ];
208 daniel-mar 216
                }
217
 
218
                // gmp_hamdist ( GMP $a , GMP $b ) : int
219
                // Hamming distance
220
                function gmp_hamdist($a, $b) {
221
                        bcscale(0);
222
                        throw new Exception("NOT IMPLEMENTED");
223
                }
224
 
225
                // gmp_import ( string $data [, int $word_size = 1 [, int $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN ]] ) : GMP
226
                // Import from a binary string
227
                function gmp_import($data, $word_size=1, $options=GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) {
228
                        bcscale(0);
229
                        throw new Exception("NOT IMPLEMENTED");
230
                }
231
 
232
                // gmp_init ( mixed $number [, int $base = 0 ] ) : GMP
233
                // Create GMP number
234
                function gmp_init($number, $base=0) {
235
                        bcscale(0);
236
                        if ($base == 0) {
237
                                // If base is 0 (default value), the actual base is determined from the leading characters:
238
                                // if the first two characters are 0x or 0X, hexadecimal is assumed,
239
                                // otherwise if the first character is "0", octal is assumed,
240
                                // otherwise decimal is assumed.
241
                                if (strtoupper(substr($number, 0, 2)) == '0X') {
242
                                        $base = 16;
243
                                } else if (strtoupper(substr($number, 0, 1)) == '0') {
244
                                        $base = 8;
245
                                } else {
246
                                        $base = 10;
247
                                }
248
                        }
249
 
250
                        if ($base == 10) {
251
                                return $number;
252
                        } else {
253
                                return base_convert_bigint($number, $base, 10);
254
                        }
255
                }
256
 
257
                // gmp_intval ( GMP $gmpnumber ) : int
258
                // Convert GMP number to integer
259
                function gmp_intval($gmpnumber) {
260
                        bcscale(0);
261
                        return (int)$gmpnumber;
262
                }
263
 
264
                // gmp_invert ( GMP $a , GMP $b ) : GMP
265
                // Inverse by modulo
266
                function gmp_invert($a, $b) {
267
                        bcscale(0);
268
                        throw new Exception("NOT IMPLEMENTED");
269
                }
270
 
271
                // gmp_jacobi ( GMP $a , GMP $p ) : int
272
                // Jacobi symbol
273
                function gmp_jacobi($a, $p) {
274
                        bcscale(0);
275
                        throw new Exception("NOT IMPLEMENTED");
276
                }
277
 
278
                // gmp_kronecker ( mixed $a , mixed $b ) : int
279
                // Kronecker symbol
280
                function gmp_kronecker($a, $b) {
281
                        bcscale(0);
282
                        throw new Exception("NOT IMPLEMENTED");
283
                }
284
 
285
                // gmp_lcm ( mixed $a , mixed $b ) : GMP
286
                // Calculate LCM
287
                function gmp_lcm($a, $b) {
288
                        bcscale(0);
289
                        throw new Exception("NOT IMPLEMENTED");
290
                }
291
 
292
                // gmp_legendre ( GMP $a , GMP $p ) : int
293
                // Legendre symbol
294
                function gmp_legendre($a, $p) {
295
                        bcscale(0);
296
                        throw new Exception("NOT IMPLEMENTED");
297
                }
298
 
299
                // gmp_mod ( GMP $n , GMP $d ) : GMP
300
                // Modulo operation
301
                function gmp_mod($n, $d) {
302
                        bcscale(0);
303
 
304
                        // bcmod ( string $dividend , string $divisor [, int $scale = 0 ] ) : string
305
                        return bcmod($n, $d);
306
                }
307
 
308
                // gmp_mul ( GMP $a , GMP $b ) : GMP
309
                // Multiply numbers
310
                function gmp_mul($a, $b) {
311
                        bcscale(0);
312
 
313
                        // bcmul ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
314
                        return bcmul($a, $b);
315
                }
316
 
317
                // gmp_neg ( GMP $a ) : GMP
318
                // Negate number
319
                function gmp_neg($a) {
320
                        bcscale(0);
321
                        return bcmul($a, "-1");
322
                }
323
 
324
                // gmp_nextprime ( int $a ) : GMP
325
                // Find next prime number
326
                function gmp_nextprime($a) {
327
                        bcscale(0);
328
                        throw new Exception("NOT IMPLEMENTED");
329
                }
330
 
331
                // gmp_or ( GMP $a , GMP $b ) : GMP
332
                // Bitwise OR
333
                function gmp_or($a, $b) {
334
                        bcscale(0);
335
                        // Convert $a and $b to a binary string
336
                        $ab = bc_dec2bin($a);
337
                        $bb = bc_dec2bin($b);
338
                        $length = max(strlen($ab), strlen($bb));
339
                        $ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
340
                        $bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
341
 
342
                        // Do the bitwise binary operation
343
                        $cb = '';
344
                        for ($i=0; $i<$length; $i++) {
345
                                $cb .= (($ab[$i] == 1) or ($bb[$i] == 1)) ? '1' : '0';
346
                        }
347
 
348
                        // Convert back to a decimal number
349
                        return bc_bin2dec($cb);
350
                }
351
 
352
                // gmp_perfect_power ( mixed $a ) : bool
353
                // Perfect power check
354
                function gmp_perfect_power($a) {
355
                        bcscale(0);
356
                        throw new Exception("NOT IMPLEMENTED");
357
                }
358
 
359
                // gmp_perfect_square ( GMP $a ) : bool
360
                // Perfect square check
361
                function gmp_perfect_square($a) {
362
                        bcscale(0);
363
                        throw new Exception("NOT IMPLEMENTED");
364
                }
365
 
366
                // gmp_popcount ( GMP $a ) : int
367
                // Population count
368
                function gmp_popcount($a) {
369
                        bcscale(0);
370
                        throw new Exception("NOT IMPLEMENTED");
371
                }
372
 
373
                // gmp_pow ( GMP $base , int $exp ) : GMP
374
                // Raise number into power
375
                function gmp_pow($base, $exp) {
376
                        bcscale(0);
377
 
378
                        // bcpow ( string $base , string $exponent [, int $scale = 0 ] ) : string
379
                        return bcpow($base, $exp);
380
                }
381
 
382
                // gmp_powm ( GMP $base , GMP $exp , GMP $mod ) : GMP
383
                // Raise number into power with modulo
384
                function gmp_powm($base, $exp, $mod) {
385
                        bcscale(0);
386
 
387
                        // bcpowmod ( string $base , string $exponent , string $modulus [, int $scale = 0 ] ) : string
388
                        return bcpowmod($base, $exp, $mod);
389
                }
390
 
391
                // gmp_prob_prime ( GMP $a [, int $reps = 10 ] ) : int
392
                // Check if number is "probably prime"
393
                function gmp_prob_prime($a, $reps=10) {
394
                        bcscale(0);
395
                        throw new Exception("NOT IMPLEMENTED");
396
                }
397
 
398
                // gmp_random_bits ( int $bits ) : GMP
399
                // Random number
400
                function gmp_random_bits($bits) {
401
                        bcscale(0);
402
                        throw new Exception("NOT IMPLEMENTED");
403
                }
404
 
405
                // gmp_random_range ( GMP $min , GMP $max ) : GMP
406
                // Random number
407
                function gmp_random_range($min, $max) {
408
                        bcscale(0);
409
                        throw new Exception("NOT IMPLEMENTED");
410
                }
411
 
412
                // gmp_random_seed ( mixed $seed ) : void
413
                // Sets the RNG seed
414
                function gmp_random_seed($seed) {
415
                        bcscale(0);
416
                        throw new Exception("NOT IMPLEMENTED");
417
                }
418
 
419
                // gmp_random ([ int $limiter = 20 ] ) : GMP
420
                // Random number (deprecated)
421
                function gmp_random($limiter=20) {
422
                        bcscale(0);
423
                        throw new Exception("NOT IMPLEMENTED");
424
                }
425
 
426
                // gmp_root ( GMP $a , int $nth ) : GMP
427
                // Take the integer part of nth root
428
                function gmp_root($a, $nth) {
429
                        bcscale(0);
430
                        throw new Exception("NOT IMPLEMENTED");
431
                }
432
 
433
                // gmp_rootrem ( GMP $a , int $nth ) : array
434
                // Take the integer part and remainder of nth root
435
                function gmp_rootrem($a, $nth) {
436
                        bcscale(0);
437
                        throw new Exception("NOT IMPLEMENTED");
438
                }
439
 
440
                // gmp_scan0 ( GMP $a , int $start ) : int
441
                // Scan for 0
442
                function gmp_scan0($a, $start) {
443
                        bcscale(0);
444
 
445
                        $ab = bc_dec2bin($a);
446
 
447
                        if ($start < 0) throw new Exception("Starting index must be greater than or equal to zero");
448
                        if ($start >= strlen($ab)) return $start;
449
 
450
                        for ($i=$start; $i<strlen($ab); $i++) {
451
                                if ($ab[strlen($ab)-1-$i] == '0') {
452
                                        return $i;
453
                                }
454
                        }
455
 
456
                        return false;
457
                }
458
 
459
                // gmp_scan1 ( GMP $a , int $start ) : int
460
                // Scan for 1
461
                function gmp_scan1($a, $start) {
462
                        bcscale(0);
463
 
464
                        $ab = bc_dec2bin($a);
465
 
466
                        if ($start < 0) throw new Exception("Starting index must be greater than or equal to zero");
467
                        if ($start >= strlen($ab)) return -1;
468
 
469
                        for ($i=$start; $i<strlen($ab); $i++) {
470
                                if ($ab[strlen($ab)-1-$i] == '1') {
471
                                        return $i;
472
                                }
473
                        }
474
 
475
                        return false;
476
                }
477
 
478
                // gmp_setbit ( GMP $a , int $index [, bool $bit_on = TRUE ] ) : void
479
                // Set bit
480
                function gmp_setbit($a, $index, $bit_on=TRUE) {
481
                        bcscale(0);
482
                        $ab = bc_dec2bin($a);
483
 
484
                        if ($index < 0) throw new Exception("Invalid index");          
485
                        if ($index >= strlen($ab)) {
486
                                $ab = str_pad($ab, $index+1, '0', STR_PAD_LEFT);
487
                        }
488
 
489
                        $ab[strlen($ab)-1-$index] = $bit_on ? '1' : '0';
490
 
491
                        return bc_bin2dec($ab);
492
                }
493
 
494
                // gmp_sign ( GMP $a ) : int
495
                // Sign of number
496
                function gmp_sign($a) {
497
                        bcscale(0);
498
                        return bccomp($a, "0");
499
                }
500
 
501
                // gmp_sqrt ( GMP $a ) : GMP
502
                // Calculate square root
503
                function gmp_sqrt($a) {
504
                        bcscale(0);
505
 
506
                        // bcsqrt ( string $operand [, int $scale = 0 ] ) : string
507
                        return bcsqrt($a);
508
                }
509
 
510
                // gmp_sqrtrem ( GMP $a ) : array
511
                // Square root with remainder
512
                function gmp_sqrtrem($a) {
513
                        bcscale(0);
514
                        throw new Exception("NOT IMPLEMENTED");
515
                }
516
 
517
                // gmp_strval ( GMP $gmpnumber [, int $base = 10 ] ) : string
518
                // Convert GMP number to string
519
                function gmp_strval($gmpnumber, $base=10) {
520
                        bcscale(0);
521
                        if ($base == 10) {
522
                                return $gmpnumber;
523
                        } else {
524
                                return base_convert_bigint($gmpnumber, 10, $base);
525
                        }
526
                }
527
 
528
                // gmp_sub ( GMP $a , GMP $b ) : GMP
529
                // Subtract numbers
530
                function gmp_sub($a, $b) {
531
                        bcscale(0);
532
 
533
                        // bcsub ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
534
                        return bcsub($a, $b);
535
                }
536
 
537
                // gmp_testbit ( GMP $a , int $index ) : bool
538
                // Tests if a bit is set
539
                function gmp_testbit($a, $index) {
540
                        bcscale(0);
541
                        $ab = bc_dec2bin($a);
542
 
543
                        if ($index < 0) throw new Exception("Invalid index");          
544
                        if ($index >= strlen($ab)) return ('0' == '1');
545
 
546
                        return $ab[strlen($ab)-1-$index] == '1';
547
                }
548
 
549
                // gmp_xor ( GMP $a , GMP $b ) : GMP
550
                // Bitwise XOR
551
                function gmp_xor($a, $b) {
552
                        bcscale(0);
553
                        // Convert $a and $b to a binary string
554
                        $ab = bc_dec2bin($a);
555
                        $bb = bc_dec2bin($b);
556
                        $length = max(strlen($ab), strlen($bb));
557
                        $ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
558
                        $bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
559
 
560
                        // Do the bitwise binary operation
561
                        $cb = '';
562
                        for ($i=0; $i<$length; $i++) {
563
                                $cb .= (($ab[$i] == 1) xor ($bb[$i] == 1)) ? '1' : '0';
564
                        }
565
 
566
                        // Convert back to a decimal number
567
                        return bc_bin2dec($cb);
568
                }
569
        }
570
 
571
        // ----------------- Helper functions -----------------
572
 
573
        function base_convert_bigint($numstring, $frombase, $tobase) {
574
                $frombase_str = '';
575
                for ($i=0; $i<$frombase; $i++) {
576
                        $frombase_str .= strtoupper(base_convert($i, 10, 36));
577
                }
578
 
579
                $tobase_str = '';
580
                for ($i=0; $i<$tobase; $i++) {
581
                        $tobase_str .= strtoupper(base_convert($i, 10, 36));
582
                }
583
 
584
                $length = strlen($numstring);
585
                $result = '';
586
                $number = array();
587
                for ($i = 0; $i < $length; $i++) {
588
                        $number[$i] = stripos($frombase_str, $numstring[$i]);
589
                }
590
                do { // Loop until whole number is converted
591
                        $divide = 0;
592
                        $newlen = 0;
593
                        for ($i = 0; $i < $length; $i++) { // Perform division manually (which is why this works with big numbers)
594
                                $divide = $divide * $frombase + $number[$i];
595
                                if ($divide >= $tobase) {
596
                                        $number[$newlen++] = (int)($divide / $tobase);
597
                                        $divide = $divide % $tobase;
598
                                } else if ($newlen > 0) {
599
                                        $number[$newlen++] = 0;
600
                                }
601
                        }
602
                        $length = $newlen;
603
                        $result = $tobase_str[$divide] . $result; // Divide is basically $numstring % $tobase (i.e. the new character)
604
                }
605
                while ($newlen != 0);
606
 
607
                return $result;
608
        }
609
 
610
        function bc_dec2bin($decimal_i) {
611
                // https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/
612
 
613
                bcscale(0);
614
 
615
                $binary_i = '';
616
                do {
617
                        $binary_i = bcmod($decimal_i,'2') . $binary_i;
618
                        $decimal_i = bcdiv($decimal_i,'2');
619
                } while (bccomp($decimal_i,'0'));
620
 
621
                return $binary_i;
622
        }
623
 
624
        function bc_bin2dec($binary_i) {
625
                // https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/
626
 
627
                bcscale(0);
628
 
629
                $decimal_i = '0';
630
                for ($i = 0; $i < strlen($binary_i); $i++) {
631
                        $decimal_i = bcmul($decimal_i,'2');
632
                        $decimal_i = bcadd($decimal_i,$binary_i[$i]);
633
                }
634
 
635
                return $decimal_i;
636
        }
637
 
638
        // ----------------- New functions ----------------- 
639
 
640
        // Newly added: gmp_not / bcnot
641
        function bcnot($a) {
642
                bcscale(0);
643
                // Convert $a and $b to a binary string
644
                $ab = bc_dec2bin($a);
645
                $bb = bc_dec2bin($b);
646
                $length = max(strlen($ab), strlen($bb));
647
                $ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
648
                $bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
649
 
650
                // Do the bitwise binary operation
651
                $cb = '';
652
                for ($i=0; $i<$length; $i++) {
653
                        if ($ab[$i] == 1) return false;
654
                }
655
 
656
                return true;
657
        }
658
        function gmp_not($a) {
659
                return bcnot($a);
660
        }
661
 
662
        // Newly added: bcshiftl / gmp_shiftl   
663
        function bcshiftl($num, $bits) {
664
                bcscale(0);
665
                return bcmul($num, bcpow('2', $bits));
666
        }
667
        function gmp_shiftl($num, $bits) {
668
                return bcshiftl($num, $bits);
669
        }
670
 
671
        // Newly added: bcshiftr / gmp_shiftr   
672
        function bcshiftr($num, $bits) {
673
                bcscale(0);
674
                return bcdiv($num, bcpow('2', $bits));
675
        }
676
        function gmp_shiftr($num, $bits) {
677
                return bcshiftr($num, $bits);
678
        }
679
 
680
        // Newly added: bcfact (used by gmp_fact)
681
        function bcfact($a) {
682
                bcscale(0);
683
 
684
                // Source: https://www.php.net/manual/de/book.bc.php#116510
685
 
686
                if (!filter_var($a, FILTER_VALIDATE_INT) || $a <= 0) {
687
                        throw new InvalidArgumentException(sprintf('Argument must be natural number, "%s" given.', $a));
688
                }
689
 
690
                for ($result = '1'; $a > 0; $a--) {
691
                        $result = bcmul($result, $a);
692
                }
693
 
694
                return $result;
695
        }
696
}