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