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 | } |