1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Stanislav Malyshev <stas@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24 #include "php_ini.h"
25 #include "php_gmp.h"
26 #include "php_gmp_int.h"
27 #include "ext/standard/info.h"
28 #include "ext/standard/php_var.h"
29 #include "zend_smart_str_public.h"
30 #include "zend_exceptions.h"
31
32 #include <gmp.h>
33
34 /* Needed for gmp_random() */
35 #include "ext/standard/php_rand.h"
36 #include "ext/standard/php_lcg.h"
37 #define GMP_ABS(x) ((x) >= 0 ? (x) : -(x))
38
39 /* {{{ arginfo */
40 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_init, 0, 0, 1)
41 ZEND_ARG_INFO(0, number)
42 ZEND_ARG_INFO(0, base)
43 ZEND_END_ARG_INFO()
44
45 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_import, 0, 0, 1)
46 ZEND_ARG_INFO(0, data)
47 ZEND_ARG_INFO(0, word_size)
48 ZEND_ARG_INFO(0, options)
49 ZEND_END_ARG_INFO()
50
51 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_export, 0, 0, 1)
52 ZEND_ARG_INFO(0, gmpnumber)
53 ZEND_ARG_INFO(0, word_size)
54 ZEND_ARG_INFO(0, options)
55 ZEND_END_ARG_INFO()
56
57 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_intval, 0, 0, 1)
58 ZEND_ARG_INFO(0, gmpnumber)
59 ZEND_END_ARG_INFO()
60
61 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_strval, 0, 0, 1)
62 ZEND_ARG_INFO(0, gmpnumber)
63 ZEND_ARG_INFO(0, base)
64 ZEND_END_ARG_INFO()
65
66 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_unary, 0, 0, 1)
67 ZEND_ARG_INFO(0, a)
68 ZEND_END_ARG_INFO()
69
70 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_binary, 0, 0, 2)
71 ZEND_ARG_INFO(0, a)
72 ZEND_ARG_INFO(0, b)
73 ZEND_END_ARG_INFO()
74
75 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_div, 0, 0, 2)
76 ZEND_ARG_INFO(0, a)
77 ZEND_ARG_INFO(0, b)
78 ZEND_ARG_INFO(0, round)
79 ZEND_END_ARG_INFO()
80
81 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_pow, 0, 0, 2)
82 ZEND_ARG_INFO(0, base)
83 ZEND_ARG_INFO(0, exp)
84 ZEND_END_ARG_INFO()
85
86 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_powm, 0, 0, 3)
87 ZEND_ARG_INFO(0, base)
88 ZEND_ARG_INFO(0, exp)
89 ZEND_ARG_INFO(0, mod)
90 ZEND_END_ARG_INFO()
91
92 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_root, 0, 0, 2)
93 ZEND_ARG_INFO(0, a)
94 ZEND_ARG_INFO(0, nth)
95 ZEND_END_ARG_INFO()
96
97 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_prob_prime, 0, 0, 1)
98 ZEND_ARG_INFO(0, a)
99 ZEND_ARG_INFO(0, reps)
100 ZEND_END_ARG_INFO()
101
102 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_random, 0, 0, 0)
103 ZEND_ARG_INFO(0, limiter)
104 ZEND_END_ARG_INFO()
105
106 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_random_seed, 0, 0, 1)
107 ZEND_ARG_INFO(0, seed)
108 ZEND_END_ARG_INFO()
109
110 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_random_bits, 0, 0, 1)
111 ZEND_ARG_INFO(0, bits)
112 ZEND_END_ARG_INFO()
113
114 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_random_range, 0, 0, 2)
115 ZEND_ARG_INFO(0, min)
116 ZEND_ARG_INFO(0, max)
117 ZEND_END_ARG_INFO()
118
119 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_setbit, 0, 0, 2)
120 ZEND_ARG_INFO(0, a)
121 ZEND_ARG_INFO(0, index)
122 ZEND_ARG_INFO(0, set_clear)
123 ZEND_END_ARG_INFO()
124
125 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_bit, 0, 0, 2)
126 ZEND_ARG_INFO(0, a)
127 ZEND_ARG_INFO(0, index)
128 ZEND_END_ARG_INFO()
129
130 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_scan, 0, 0, 2)
131 ZEND_ARG_INFO(0, a)
132 ZEND_ARG_INFO(0, start)
133 ZEND_END_ARG_INFO()
134
135 /* }}} */
136
137 ZEND_DECLARE_MODULE_GLOBALS(gmp)
138 static ZEND_GINIT_FUNCTION(gmp);
139
140 /* {{{ gmp_functions[]
141 */
142 static const zend_function_entry gmp_functions[] = {
143 ZEND_FE(gmp_init, arginfo_gmp_init)
144 ZEND_FE(gmp_import, arginfo_gmp_import)
145 ZEND_FE(gmp_export, arginfo_gmp_export)
146 ZEND_FE(gmp_intval, arginfo_gmp_intval)
147 ZEND_FE(gmp_strval, arginfo_gmp_strval)
148 ZEND_FE(gmp_add, arginfo_gmp_binary)
149 ZEND_FE(gmp_sub, arginfo_gmp_binary)
150 ZEND_FE(gmp_mul, arginfo_gmp_binary)
151 ZEND_FE(gmp_div_qr, arginfo_gmp_div)
152 ZEND_FE(gmp_div_q, arginfo_gmp_div)
153 ZEND_FE(gmp_div_r, arginfo_gmp_div)
154 ZEND_FALIAS(gmp_div, gmp_div_q, arginfo_gmp_div)
155 ZEND_FE(gmp_mod, arginfo_gmp_binary)
156 ZEND_FE(gmp_divexact, arginfo_gmp_binary)
157 ZEND_FE(gmp_neg, arginfo_gmp_unary)
158 ZEND_FE(gmp_abs, arginfo_gmp_unary)
159 ZEND_FE(gmp_fact, arginfo_gmp_unary)
160 ZEND_FE(gmp_sqrt, arginfo_gmp_unary)
161 ZEND_FE(gmp_sqrtrem, arginfo_gmp_unary)
162 ZEND_FE(gmp_root, arginfo_gmp_root)
163 ZEND_FE(gmp_rootrem, arginfo_gmp_root)
164 ZEND_FE(gmp_pow, arginfo_gmp_pow)
165 ZEND_FE(gmp_powm, arginfo_gmp_powm)
166 ZEND_FE(gmp_perfect_square, arginfo_gmp_unary)
167 ZEND_FE(gmp_perfect_power, arginfo_gmp_unary)
168 ZEND_FE(gmp_prob_prime, arginfo_gmp_prob_prime)
169 ZEND_FE(gmp_gcd, arginfo_gmp_binary)
170 ZEND_FE(gmp_gcdext, arginfo_gmp_binary)
171 ZEND_FE(gmp_lcm, arginfo_gmp_binary)
172 ZEND_FE(gmp_invert, arginfo_gmp_binary)
173 ZEND_FE(gmp_jacobi, arginfo_gmp_binary)
174 ZEND_FE(gmp_legendre, arginfo_gmp_binary)
175 ZEND_FE(gmp_kronecker, arginfo_gmp_binary)
176 ZEND_FE(gmp_cmp, arginfo_gmp_binary)
177 ZEND_FE(gmp_sign, arginfo_gmp_unary)
178 ZEND_DEP_FE(gmp_random, arginfo_gmp_random)
179 ZEND_FE(gmp_random_seed, arginfo_gmp_random_seed)
180 ZEND_FE(gmp_random_bits, arginfo_gmp_random_bits)
181 ZEND_FE(gmp_random_range, arginfo_gmp_random_range)
182 ZEND_FE(gmp_and, arginfo_gmp_binary)
183 ZEND_FE(gmp_or, arginfo_gmp_binary)
184 ZEND_FE(gmp_com, arginfo_gmp_unary)
185 ZEND_FE(gmp_xor, arginfo_gmp_binary)
186 ZEND_FE(gmp_setbit, arginfo_gmp_setbit)
187 ZEND_FE(gmp_clrbit, arginfo_gmp_bit)
188 ZEND_FE(gmp_testbit, arginfo_gmp_bit)
189 ZEND_FE(gmp_scan0, arginfo_gmp_scan)
190 ZEND_FE(gmp_scan1, arginfo_gmp_scan)
191 ZEND_FE(gmp_popcount, arginfo_gmp_unary)
192 ZEND_FE(gmp_hamdist, arginfo_gmp_binary)
193 ZEND_FE(gmp_nextprime, arginfo_gmp_unary)
194 ZEND_FE(gmp_binomial, arginfo_gmp_binary)
195 PHP_FE_END
196 };
197 /* }}} */
198
199 /* {{{ gmp_module_entry
200 */
201 zend_module_entry gmp_module_entry = {
202 STANDARD_MODULE_HEADER,
203 "gmp",
204 gmp_functions,
205 ZEND_MODULE_STARTUP_N(gmp),
206 NULL,
207 NULL,
208 ZEND_MODULE_DEACTIVATE_N(gmp),
209 ZEND_MODULE_INFO_N(gmp),
210 PHP_GMP_VERSION,
211 ZEND_MODULE_GLOBALS(gmp),
212 ZEND_GINIT(gmp),
213 NULL,
214 NULL,
215 STANDARD_MODULE_PROPERTIES_EX
216 };
217 /* }}} */
218
219 #ifdef COMPILE_DL_GMP
220 #ifdef ZTS
221 ZEND_TSRMLS_CACHE_DEFINE()
222 #endif
223 ZEND_GET_MODULE(gmp)
224 #endif
225
226 static zend_class_entry *gmp_ce;
227 static zend_object_handlers gmp_object_handlers;
228
php_gmp_class_entry()229 PHP_GMP_API zend_class_entry *php_gmp_class_entry() {
230 return gmp_ce;
231 }
232
233 typedef struct _gmp_temp {
234 mpz_t num;
235 zend_bool is_used;
236 } gmp_temp_t;
237
238 #define GMP_ROUND_ZERO 0
239 #define GMP_ROUND_PLUSINF 1
240 #define GMP_ROUND_MINUSINF 2
241
242 #define GMP_MSW_FIRST (1 << 0)
243 #define GMP_LSW_FIRST (1 << 1)
244 #define GMP_LITTLE_ENDIAN (1 << 2)
245 #define GMP_BIG_ENDIAN (1 << 3)
246 #define GMP_NATIVE_ENDIAN (1 << 4)
247
248 #define GMP_MAX_BASE 62
249
250 #define GMP_51_OR_NEWER \
251 ((__GNU_MP_VERSION >= 6) || (__GNU_MP_VERSION >= 5 && __GNU_MP_VERSION_MINOR >= 1))
252
253 #define IS_GMP(zval) \
254 (Z_TYPE_P(zval) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zval), gmp_ce))
255
256 #define GET_GMP_OBJECT_FROM_OBJ(obj) \
257 php_gmp_object_from_zend_object(obj)
258 #define GET_GMP_OBJECT_FROM_ZVAL(zv) \
259 GET_GMP_OBJECT_FROM_OBJ(Z_OBJ_P(zv))
260
261 #define GET_GMP_FROM_ZVAL(zval) \
262 GET_GMP_OBJECT_FROM_OBJ(Z_OBJ_P(zval))->num
263
264 /* The FETCH_GMP_ZVAL_* family of macros is used to fetch a gmp number
265 * (mpz_ptr) from a zval. If the zval is not a GMP instance, then we
266 * try to convert the value to a temporary gmp number using convert_to_gmp.
267 * This temporary number is stored in the temp argument, which is of type
268 * gmp_temp_t. This temporary value needs to be freed lateron using the
269 * FREE_GMP_TEMP macro.
270 *
271 * If the conversion to a gmp number fails, the macros return false.
272 * The _DEP / _DEP_DEP variants additionally free the temporary values
273 * passed in the last / last two arguments.
274 *
275 * If one zval can sometimes be fetched as a long you have to set the
276 * is_used member of the corresponding gmp_temp_t value to 0, otherwise
277 * the FREE_GMP_TEMP and *_DEP macros will not work properly.
278 *
279 * The three FETCH_GMP_ZVAL_* macros below are mostly copy & paste code
280 * as I couldn't find a way to combine them.
281 */
282
283 #define FREE_GMP_TEMP(temp) \
284 if (temp.is_used) { \
285 mpz_clear(temp.num); \
286 }
287
288 #define FETCH_GMP_ZVAL_DEP_DEP(gmpnumber, zval, temp, dep1, dep2) \
289 if (IS_GMP(zval)) { \
290 gmpnumber = GET_GMP_FROM_ZVAL(zval); \
291 temp.is_used = 0; \
292 } else { \
293 mpz_init(temp.num); \
294 if (convert_to_gmp(temp.num, zval, 0) == FAILURE) { \
295 mpz_clear(temp.num); \
296 FREE_GMP_TEMP(dep1); \
297 FREE_GMP_TEMP(dep2); \
298 RETURN_FALSE; \
299 } \
300 temp.is_used = 1; \
301 gmpnumber = temp.num; \
302 }
303
304 #define FETCH_GMP_ZVAL_DEP(gmpnumber, zval, temp, dep) \
305 if (IS_GMP(zval)) { \
306 gmpnumber = GET_GMP_FROM_ZVAL(zval); \
307 temp.is_used = 0; \
308 } else { \
309 mpz_init(temp.num); \
310 if (convert_to_gmp(temp.num, zval, 0) == FAILURE) { \
311 mpz_clear(temp.num); \
312 FREE_GMP_TEMP(dep); \
313 RETURN_FALSE; \
314 } \
315 temp.is_used = 1; \
316 gmpnumber = temp.num; \
317 }
318
319 #define FETCH_GMP_ZVAL(gmpnumber, zval, temp) \
320 if (IS_GMP(zval)) { \
321 gmpnumber = GET_GMP_FROM_ZVAL(zval); \
322 temp.is_used = 0; \
323 } else { \
324 mpz_init(temp.num); \
325 if (convert_to_gmp(temp.num, zval, 0) == FAILURE) { \
326 mpz_clear(temp.num); \
327 RETURN_FALSE; \
328 } \
329 temp.is_used = 1; \
330 gmpnumber = temp.num; \
331 }
332
333 #define INIT_GMP_RETVAL(gmpnumber) \
334 gmp_create(return_value, &gmpnumber)
335
336 static void gmp_strval(zval *result, mpz_t gmpnum, int base);
337 static int convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base);
338 static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg);
339
340 /*
341 * The gmp_*_op functions provide an implementation for several common types
342 * of GMP functions. The gmp_zval_(unary|binary)_*_op functions have to be manually
343 * passed zvals to work on, whereas the gmp_(unary|binary)_*_op macros already
344 * include parameter parsing.
345 */
346 typedef void (*gmp_unary_op_t)(mpz_ptr, mpz_srcptr);
347 typedef int (*gmp_unary_opl_t)(mpz_srcptr);
348
349 typedef void (*gmp_unary_ui_op_t)(mpz_ptr, gmp_ulong);
350
351 typedef void (*gmp_binary_op_t)(mpz_ptr, mpz_srcptr, mpz_srcptr);
352 typedef int (*gmp_binary_opl_t)(mpz_srcptr, mpz_srcptr);
353
354 typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong);
355 typedef void (*gmp_binary_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr);
356 typedef void (*gmp_binary_ui_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, gmp_ulong);
357
358 static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, int check_b_zero);
359 static inline void gmp_zval_binary_ui_op2(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int check_b_zero);
360 static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op);
361 static inline void gmp_zval_unary_ui_op(zval *return_value, zval *a_arg, gmp_unary_ui_op_t gmp_op);
362
363 /* Binary operations */
364 #define gmp_binary_ui_op(op, uop) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop, 0)
365 #define gmp_binary_op(op) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, NULL, 0)
366 #define gmp_binary_opl(op) _gmp_binary_opl(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
367 #define gmp_binary_ui_op_no_zero(op, uop) \
368 _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop, 1)
369
370 /* Unary operations */
371 #define gmp_unary_op(op) _gmp_unary_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
372 #define gmp_unary_opl(op) _gmp_unary_opl(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
373
gmp_free_object_storage(zend_object * obj)374 static void gmp_free_object_storage(zend_object *obj) /* {{{ */
375 {
376 gmp_object *intern = GET_GMP_OBJECT_FROM_OBJ(obj);
377
378 mpz_clear(intern->num);
379 zend_object_std_dtor(&intern->std);
380 }
381 /* }}} */
382
gmp_create_object_ex(zend_class_entry * ce,mpz_ptr * gmpnum_target)383 static inline zend_object *gmp_create_object_ex(zend_class_entry *ce, mpz_ptr *gmpnum_target) /* {{{ */
384 {
385 gmp_object *intern = emalloc(sizeof(gmp_object) + zend_object_properties_size(ce));
386
387 zend_object_std_init(&intern->std, ce);
388 object_properties_init(&intern->std, ce);
389
390 mpz_init(intern->num);
391 *gmpnum_target = intern->num;
392 intern->std.handlers = &gmp_object_handlers;
393
394 return &intern->std;
395 }
396 /* }}} */
397
gmp_create_object(zend_class_entry * ce)398 static zend_object *gmp_create_object(zend_class_entry *ce) /* {{{ */
399 {
400 mpz_ptr gmpnum_dummy;
401 return gmp_create_object_ex(ce, &gmpnum_dummy);
402 }
403 /* }}} */
404
gmp_create(zval * target,mpz_ptr * gmpnum_target)405 static inline void gmp_create(zval *target, mpz_ptr *gmpnum_target) /* {{{ */
406 {
407 ZVAL_OBJ(target, gmp_create_object_ex(gmp_ce, gmpnum_target));
408 }
409 /* }}} */
410
gmp_cast_object(zval * readobj,zval * writeobj,int type)411 static int gmp_cast_object(zval *readobj, zval *writeobj, int type) /* {{{ */
412 {
413 mpz_ptr gmpnum;
414 switch (type) {
415 case IS_STRING:
416 gmpnum = GET_GMP_FROM_ZVAL(readobj);
417 gmp_strval(writeobj, gmpnum, 10);
418 return SUCCESS;
419 case IS_LONG:
420 gmpnum = GET_GMP_FROM_ZVAL(readobj);
421 ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
422 return SUCCESS;
423 case IS_DOUBLE:
424 gmpnum = GET_GMP_FROM_ZVAL(readobj);
425 ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
426 return SUCCESS;
427 case _IS_NUMBER:
428 gmpnum = GET_GMP_FROM_ZVAL(readobj);
429 if (mpz_fits_slong_p(gmpnum)) {
430 ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
431 } else {
432 ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
433 }
434 return SUCCESS;
435 default:
436 return FAILURE;
437 }
438 }
439 /* }}} */
440
gmp_get_debug_info(zval * obj,int * is_temp)441 static HashTable *gmp_get_debug_info(zval *obj, int *is_temp) /* {{{ */
442 {
443 HashTable *ht, *props = zend_std_get_properties(obj);
444 mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(obj);
445 zval zv;
446
447 *is_temp = 1;
448 ht = zend_array_dup(props);
449
450 gmp_strval(&zv, gmpnum, 10);
451 zend_hash_str_update(ht, "num", sizeof("num")-1, &zv);
452
453 return ht;
454 }
455 /* }}} */
456
gmp_clone_obj(zval * obj)457 static zend_object *gmp_clone_obj(zval *obj) /* {{{ */
458 {
459 gmp_object *old_object = GET_GMP_OBJECT_FROM_ZVAL(obj);
460 gmp_object *new_object = GET_GMP_OBJECT_FROM_OBJ(gmp_create_object(Z_OBJCE_P(obj)));
461
462 zend_objects_clone_members( &new_object->std, &old_object->std);
463
464 mpz_set(new_object->num, old_object->num);
465
466 return &new_object->std;
467 }
468 /* }}} */
469
shift_operator_helper(gmp_binary_ui_op_t op,zval * return_value,zval * op1,zval * op2)470 static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2) {
471 zend_long shift = zval_get_long(op2);
472
473 if (shift < 0) {
474 php_error_docref(NULL, E_WARNING, "Shift cannot be negative");
475 RETVAL_FALSE;
476 } else {
477 mpz_ptr gmpnum_op, gmpnum_result;
478 gmp_temp_t temp;
479
480 FETCH_GMP_ZVAL(gmpnum_op, op1, temp);
481 INIT_GMP_RETVAL(gmpnum_result);
482 op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
483 FREE_GMP_TEMP(temp);
484 }
485 }
486
487 #define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \
488 gmp_zval_binary_ui_op( \
489 result, op1, op2, op, (gmp_binary_ui_op_t) uop, \
490 check_b_zero \
491 ); \
492 return SUCCESS;
493
494 #define DO_BINARY_UI_OP(op) DO_BINARY_UI_OP_EX(op, op ## _ui, 0)
495 #define DO_BINARY_OP(op) DO_BINARY_UI_OP_EX(op, NULL, 0)
496
497 #define DO_UNARY_OP(op) \
498 gmp_zval_unary_op(result, op1, op); \
499 return SUCCESS;
500
gmp_do_operation_ex(zend_uchar opcode,zval * result,zval * op1,zval * op2)501 static int gmp_do_operation_ex(zend_uchar opcode, zval *result, zval *op1, zval *op2) /* {{{ */
502 {
503 switch (opcode) {
504 case ZEND_ADD:
505 DO_BINARY_UI_OP(mpz_add);
506 case ZEND_SUB:
507 DO_BINARY_UI_OP(mpz_sub);
508 case ZEND_MUL:
509 DO_BINARY_UI_OP(mpz_mul);
510 case ZEND_POW:
511 shift_operator_helper(mpz_pow_ui, result, op1, op2);
512 return SUCCESS;
513 case ZEND_DIV:
514 DO_BINARY_UI_OP_EX(mpz_tdiv_q, mpz_tdiv_q_ui, 1);
515 case ZEND_MOD:
516 DO_BINARY_UI_OP_EX(mpz_mod, mpz_mod_ui, 1);
517 case ZEND_SL:
518 shift_operator_helper(mpz_mul_2exp, result, op1, op2);
519 return SUCCESS;
520 case ZEND_SR:
521 shift_operator_helper(mpz_fdiv_q_2exp, result, op1, op2);
522 return SUCCESS;
523 case ZEND_BW_OR:
524 DO_BINARY_OP(mpz_ior);
525 case ZEND_BW_AND:
526 DO_BINARY_OP(mpz_and);
527 case ZEND_BW_XOR:
528 DO_BINARY_OP(mpz_xor);
529 case ZEND_BW_NOT:
530 DO_UNARY_OP(mpz_com);
531
532 default:
533 return FAILURE;
534 }
535 }
536 /* }}} */
537
gmp_do_operation(zend_uchar opcode,zval * result,zval * op1,zval * op2)538 static int gmp_do_operation(zend_uchar opcode, zval *result, zval *op1, zval *op2) /* {{{ */
539 {
540 zval op1_copy;
541 int retval;
542
543 if (result == op1) {
544 ZVAL_COPY_VALUE(&op1_copy, op1);
545 op1 = &op1_copy;
546 }
547
548 retval = gmp_do_operation_ex(opcode, result, op1, op2);
549
550 if (retval == SUCCESS && op1 == &op1_copy) {
551 zval_ptr_dtor(op1);
552 }
553
554 return retval;
555 }
556 /* }}} */
557
gmp_compare(zval * result,zval * op1,zval * op2)558 static int gmp_compare(zval *result, zval *op1, zval *op2) /* {{{ */
559 {
560 gmp_cmp(result, op1, op2);
561 if (Z_TYPE_P(result) == IS_FALSE) {
562 ZVAL_LONG(result, 1);
563 }
564 return SUCCESS;
565 }
566 /* }}} */
567
gmp_serialize(zval * object,unsigned char ** buffer,size_t * buf_len,zend_serialize_data * data)568 static int gmp_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data) /* {{{ */
569 {
570 mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(object);
571 smart_str buf = {0};
572 zval zv;
573 php_serialize_data_t serialize_data;
574
575 PHP_VAR_SERIALIZE_INIT(serialize_data);
576
577 gmp_strval(&zv, gmpnum, 10);
578 php_var_serialize(&buf, &zv, &serialize_data);
579 zval_ptr_dtor_str(&zv);
580
581 ZVAL_ARR(&zv, zend_std_get_properties(object));
582 php_var_serialize(&buf, &zv, &serialize_data);
583
584 PHP_VAR_SERIALIZE_DESTROY(serialize_data);
585 *buffer = (unsigned char *) estrndup(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
586 *buf_len = ZSTR_LEN(buf.s);
587 zend_string_release_ex(buf.s, 0);
588
589 return SUCCESS;
590 }
591 /* }}} */
592
gmp_unserialize(zval * object,zend_class_entry * ce,const unsigned char * buf,size_t buf_len,zend_unserialize_data * data)593 static int gmp_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data) /* {{{ */
594 {
595 mpz_ptr gmpnum;
596 const unsigned char *p, *max;
597 zval *zv;
598 int retval = FAILURE;
599 php_unserialize_data_t unserialize_data;
600 zval object_copy;
601
602 PHP_VAR_UNSERIALIZE_INIT(unserialize_data);
603 gmp_create(object, &gmpnum);
604
605 /* The "object" variable may be modified during the execution of this unserialize handler
606 * (it may turn into a reference). Keep the original object around for further operations. */
607 ZVAL_OBJ(&object_copy, Z_OBJ_P(object));
608
609 p = buf;
610 max = buf + buf_len;
611
612 zv = var_tmp_var(&unserialize_data);
613 if (!php_var_unserialize(zv, &p, max, &unserialize_data)
614 || Z_TYPE_P(zv) != IS_STRING
615 || convert_to_gmp(gmpnum, zv, 10) == FAILURE
616 ) {
617 zend_throw_exception(NULL, "Could not unserialize number", 0);
618 goto exit;
619 }
620
621 zv = var_tmp_var(&unserialize_data);
622 if (!php_var_unserialize(zv, &p, max, &unserialize_data)
623 || Z_TYPE_P(zv) != IS_ARRAY
624 ) {
625 zend_throw_exception(NULL, "Could not unserialize properties", 0);
626 goto exit;
627 }
628
629 if (zend_hash_num_elements(Z_ARRVAL_P(zv)) != 0) {
630 zend_hash_copy(
631 zend_std_get_properties(&object_copy), Z_ARRVAL_P(zv),
632 (copy_ctor_func_t) zval_add_ref
633 );
634 }
635
636 retval = SUCCESS;
637 exit:
638 PHP_VAR_UNSERIALIZE_DESTROY(unserialize_data);
639 return retval;
640 }
641 /* }}} */
642
643 /* {{{ ZEND_GINIT_FUNCTION
644 */
ZEND_GINIT_FUNCTION(gmp)645 static ZEND_GINIT_FUNCTION(gmp)
646 {
647 #if defined(COMPILE_DL_GMP) && defined(ZTS)
648 ZEND_TSRMLS_CACHE_UPDATE();
649 #endif
650 gmp_globals->rand_initialized = 0;
651 }
652 /* }}} */
653
654 /* {{{ ZEND_MINIT_FUNCTION
655 */
ZEND_MINIT_FUNCTION(gmp)656 ZEND_MINIT_FUNCTION(gmp)
657 {
658 zend_class_entry tmp_ce;
659 INIT_CLASS_ENTRY(tmp_ce, "GMP", NULL);
660 gmp_ce = zend_register_internal_class(&tmp_ce);
661 gmp_ce->create_object = gmp_create_object;
662 gmp_ce->serialize = gmp_serialize;
663 gmp_ce->unserialize = gmp_unserialize;
664
665 memcpy(&gmp_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
666 gmp_object_handlers.offset = XtOffsetOf(gmp_object, std);
667 gmp_object_handlers.free_obj = gmp_free_object_storage;
668 gmp_object_handlers.cast_object = gmp_cast_object;
669 gmp_object_handlers.get_debug_info = gmp_get_debug_info;
670 gmp_object_handlers.clone_obj = gmp_clone_obj;
671 gmp_object_handlers.do_operation = gmp_do_operation;
672 gmp_object_handlers.compare = gmp_compare;
673
674 REGISTER_LONG_CONSTANT("GMP_ROUND_ZERO", GMP_ROUND_ZERO, CONST_CS | CONST_PERSISTENT);
675 REGISTER_LONG_CONSTANT("GMP_ROUND_PLUSINF", GMP_ROUND_PLUSINF, CONST_CS | CONST_PERSISTENT);
676 REGISTER_LONG_CONSTANT("GMP_ROUND_MINUSINF", GMP_ROUND_MINUSINF, CONST_CS | CONST_PERSISTENT);
677 #ifdef mpir_version
678 REGISTER_STRING_CONSTANT("GMP_MPIR_VERSION", (char *)mpir_version, CONST_CS | CONST_PERSISTENT);
679 #endif
680 REGISTER_STRING_CONSTANT("GMP_VERSION", (char *)gmp_version, CONST_CS | CONST_PERSISTENT);
681
682 REGISTER_LONG_CONSTANT("GMP_MSW_FIRST", GMP_MSW_FIRST, CONST_CS | CONST_PERSISTENT);
683 REGISTER_LONG_CONSTANT("GMP_LSW_FIRST", GMP_LSW_FIRST, CONST_CS | CONST_PERSISTENT);
684 REGISTER_LONG_CONSTANT("GMP_LITTLE_ENDIAN", GMP_LITTLE_ENDIAN, CONST_CS | CONST_PERSISTENT);
685 REGISTER_LONG_CONSTANT("GMP_BIG_ENDIAN", GMP_BIG_ENDIAN, CONST_CS | CONST_PERSISTENT);
686 REGISTER_LONG_CONSTANT("GMP_NATIVE_ENDIAN", GMP_NATIVE_ENDIAN, CONST_CS | CONST_PERSISTENT);
687
688 return SUCCESS;
689 }
690 /* }}} */
691
692 /* {{{ ZEND_RSHUTDOWN_FUNCTION
693 */
ZEND_MODULE_DEACTIVATE_D(gmp)694 ZEND_MODULE_DEACTIVATE_D(gmp)
695 {
696 if (GMPG(rand_initialized)) {
697 gmp_randclear(GMPG(rand_state));
698 GMPG(rand_initialized) = 0;
699 }
700
701 return SUCCESS;
702 }
703 /* }}} */
704
705 /* {{{ ZEND_MINFO_FUNCTION
706 */
ZEND_MODULE_INFO_D(gmp)707 ZEND_MODULE_INFO_D(gmp)
708 {
709 php_info_print_table_start();
710 php_info_print_table_row(2, "gmp support", "enabled");
711 #ifdef mpir_version
712 php_info_print_table_row(2, "MPIR version", mpir_version);
713 #else
714 php_info_print_table_row(2, "GMP version", gmp_version);
715 #endif
716 php_info_print_table_end();
717 }
718 /* }}} */
719
720
721 /* {{{ convert_to_gmp
722 * Convert zval to be gmp number */
convert_to_gmp(mpz_t gmpnumber,zval * val,zend_long base)723 static int convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base)
724 {
725 switch (Z_TYPE_P(val)) {
726 case IS_LONG:
727 case IS_FALSE:
728 case IS_TRUE: {
729 mpz_set_si(gmpnumber, zval_get_long(val));
730 return SUCCESS;
731 }
732 case IS_STRING: {
733 char *numstr = Z_STRVAL_P(val);
734 zend_bool skip_lead = 0;
735 int ret;
736
737 if (Z_STRLEN_P(val) > 2 && numstr[0] == '0') {
738 if ((base == 0 || base == 16) && (numstr[1] == 'x' || numstr[1] == 'X')) {
739 base = 16;
740 skip_lead = 1;
741 } else if ((base == 0 || base == 2) && (numstr[1] == 'b' || numstr[1] == 'B')) {
742 base = 2;
743 skip_lead = 1;
744 }
745 }
746
747 ret = mpz_set_str(gmpnumber, (skip_lead ? &numstr[2] : numstr), (int) base);
748 if (-1 == ret) {
749 php_error_docref(NULL, E_WARNING,
750 "Unable to convert variable to GMP - string is not an integer");
751 return FAILURE;
752 }
753
754 return SUCCESS;
755 }
756 default:
757 php_error_docref(NULL, E_WARNING,
758 "Unable to convert variable to GMP - wrong type");
759 return FAILURE;
760 }
761 }
762 /* }}} */
763
gmp_strval(zval * result,mpz_t gmpnum,int base)764 static void gmp_strval(zval *result, mpz_t gmpnum, int base) /* {{{ */
765 {
766 size_t num_len;
767 zend_string *str;
768
769 num_len = mpz_sizeinbase(gmpnum, abs(base));
770 if (mpz_sgn(gmpnum) < 0) {
771 num_len++;
772 }
773
774 str = zend_string_alloc(num_len, 0);
775 mpz_get_str(ZSTR_VAL(str), base, gmpnum);
776
777 /*
778 * From GMP documentation for mpz_sizeinbase():
779 * The returned value will be exact or 1 too big. If base is a power of
780 * 2, the returned value will always be exact.
781 *
782 * So let's check to see if we already have a \0 byte...
783 */
784
785 if (ZSTR_VAL(str)[ZSTR_LEN(str) - 1] == '\0') {
786 ZSTR_LEN(str)--;
787 } else {
788 ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
789 }
790
791 ZVAL_NEW_STR(result, str);
792 }
793 /* }}} */
794
gmp_cmp(zval * return_value,zval * a_arg,zval * b_arg)795 static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg) /* {{{ */
796 {
797 mpz_ptr gmpnum_a, gmpnum_b;
798 gmp_temp_t temp_a, temp_b;
799 zend_bool use_si = 0;
800 zend_long res;
801
802 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
803
804 if (Z_TYPE_P(b_arg) == IS_LONG) {
805 use_si = 1;
806 temp_b.is_used = 0;
807 } else {
808 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a);
809 }
810
811 if (use_si) {
812 res = mpz_cmp_si(gmpnum_a, Z_LVAL_P(b_arg));
813 } else {
814 res = mpz_cmp(gmpnum_a, gmpnum_b);
815 }
816
817 FREE_GMP_TEMP(temp_a);
818 FREE_GMP_TEMP(temp_b);
819
820 RETURN_LONG(res);
821 }
822 /* }}} */
823
824 /* {{{ gmp_zval_binary_ui_op
825 Execute GMP binary operation.
826 */
gmp_zval_binary_ui_op(zval * return_value,zval * a_arg,zval * b_arg,gmp_binary_op_t gmp_op,gmp_binary_ui_op_t gmp_ui_op,int check_b_zero)827 static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, int check_b_zero)
828 {
829 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
830 gmp_temp_t temp_a, temp_b;
831
832 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
833
834 if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
835 gmpnum_b = NULL;
836 temp_b.is_used = 0;
837 } else {
838 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a);
839 }
840
841 if (check_b_zero) {
842 int b_is_zero = 0;
843 if (!gmpnum_b) {
844 b_is_zero = (Z_LVAL_P(b_arg) == 0);
845 } else {
846 b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
847 }
848
849 if (b_is_zero) {
850 php_error_docref(NULL, E_WARNING, "Zero operand not allowed");
851 FREE_GMP_TEMP(temp_a);
852 FREE_GMP_TEMP(temp_b);
853 RETURN_FALSE;
854 }
855 }
856
857 INIT_GMP_RETVAL(gmpnum_result);
858
859 if (!gmpnum_b) {
860 gmp_ui_op(gmpnum_result, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
861 } else {
862 gmp_op(gmpnum_result, gmpnum_a, gmpnum_b);
863 }
864
865 FREE_GMP_TEMP(temp_a);
866 FREE_GMP_TEMP(temp_b);
867 }
868 /* }}} */
869
870 /* {{{ gmp_zval_binary_ui_op2
871 Execute GMP binary operation which returns 2 values.
872 */
gmp_zval_binary_ui_op2(zval * return_value,zval * a_arg,zval * b_arg,gmp_binary_op2_t gmp_op,gmp_binary_ui_op2_t gmp_ui_op,int check_b_zero)873 static inline void gmp_zval_binary_ui_op2(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int check_b_zero)
874 {
875 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result1, gmpnum_result2;
876 gmp_temp_t temp_a, temp_b;
877 zval result1, result2;
878
879 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
880
881 if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
882 gmpnum_b = NULL;
883 temp_b.is_used = 0;
884 } else {
885 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a);
886 }
887
888 if (check_b_zero) {
889 int b_is_zero = 0;
890 if (!gmpnum_b) {
891 b_is_zero = (Z_LVAL_P(b_arg) == 0);
892 } else {
893 b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
894 }
895
896 if (b_is_zero) {
897 php_error_docref(NULL, E_WARNING, "Zero operand not allowed");
898 FREE_GMP_TEMP(temp_a);
899 FREE_GMP_TEMP(temp_b);
900 RETURN_FALSE;
901 }
902 }
903
904 gmp_create(&result1, &gmpnum_result1);
905 gmp_create(&result2, &gmpnum_result2);
906
907 array_init(return_value);
908 add_next_index_zval(return_value, &result1);
909 add_next_index_zval(return_value, &result2);
910
911 if (!gmpnum_b) {
912 gmp_ui_op(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
913 } else {
914 gmp_op(gmpnum_result1, gmpnum_result2, gmpnum_a, gmpnum_b);
915 }
916
917 FREE_GMP_TEMP(temp_a);
918 FREE_GMP_TEMP(temp_b);
919 }
920 /* }}} */
921
922 /* {{{ _gmp_binary_ui_op
923 */
_gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS,gmp_binary_op_t gmp_op,gmp_binary_ui_op_t gmp_ui_op,int check_b_zero)924 static inline void _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, int check_b_zero)
925 {
926 zval *a_arg, *b_arg;
927
928 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
929 return;
930 }
931
932 gmp_zval_binary_ui_op(return_value, a_arg, b_arg, gmp_op, gmp_ui_op, check_b_zero);
933 }
934 /* }}} */
935
936 /* Unary operations */
937
938 /* {{{ gmp_zval_unary_op
939 */
gmp_zval_unary_op(zval * return_value,zval * a_arg,gmp_unary_op_t gmp_op)940 static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op)
941 {
942 mpz_ptr gmpnum_a, gmpnum_result;
943 gmp_temp_t temp_a;
944
945 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
946
947 INIT_GMP_RETVAL(gmpnum_result);
948 gmp_op(gmpnum_result, gmpnum_a);
949
950 FREE_GMP_TEMP(temp_a);
951 }
952 /* }}} */
953
954 /* {{{ gmp_zval_unary_ui_op
955 */
gmp_zval_unary_ui_op(zval * return_value,zval * a_arg,gmp_unary_ui_op_t gmp_op)956 static inline void gmp_zval_unary_ui_op(zval *return_value, zval *a_arg, gmp_unary_ui_op_t gmp_op)
957 {
958 mpz_ptr gmpnum_result;
959
960 INIT_GMP_RETVAL(gmpnum_result);
961 gmp_op(gmpnum_result, zval_get_long(a_arg));
962 }
963 /* }}} */
964
965 /* {{{ _gmp_unary_op
966 */
_gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS,gmp_unary_op_t gmp_op)967 static inline void _gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_op_t gmp_op)
968 {
969 zval *a_arg;
970
971 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
972 return;
973 }
974
975 gmp_zval_unary_op(return_value, a_arg, gmp_op);
976 }
977 /* }}} */
978
979 /* {{{ _gmp_unary_opl
980 */
_gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS,gmp_unary_opl_t gmp_op)981 static inline void _gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_opl_t gmp_op)
982 {
983 zval *a_arg;
984 mpz_ptr gmpnum_a;
985 gmp_temp_t temp_a;
986
987 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
988 return;
989 }
990
991 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
992 RETVAL_LONG(gmp_op(gmpnum_a));
993 FREE_GMP_TEMP(temp_a);
994 }
995 /* }}} */
996
997 /* {{{ _gmp_binary_opl
998 */
_gmp_binary_opl(INTERNAL_FUNCTION_PARAMETERS,gmp_binary_opl_t gmp_op)999 static inline void _gmp_binary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_binary_opl_t gmp_op)
1000 {
1001 zval *a_arg, *b_arg;
1002 mpz_ptr gmpnum_a, gmpnum_b;
1003 gmp_temp_t temp_a, temp_b;
1004
1005 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1006 return;
1007 }
1008
1009 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1010 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a);
1011
1012 RETVAL_LONG(gmp_op(gmpnum_a, gmpnum_b));
1013
1014 FREE_GMP_TEMP(temp_a);
1015 FREE_GMP_TEMP(temp_b);
1016 }
1017 /* }}} */
1018
1019 /* {{{ proto GMP gmp_init(mixed number [, int base])
1020 Initializes GMP number */
ZEND_FUNCTION(gmp_init)1021 ZEND_FUNCTION(gmp_init)
1022 {
1023 zval *number_arg;
1024 mpz_ptr gmpnumber;
1025 zend_long base = 0;
1026
1027 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &number_arg, &base) == FAILURE) {
1028 return;
1029 }
1030
1031 if (base && (base < 2 || base > GMP_MAX_BASE)) {
1032 php_error_docref(NULL, E_WARNING, "Bad base for conversion: " ZEND_LONG_FMT " (should be between 2 and %d)", base, GMP_MAX_BASE);
1033 RETURN_FALSE;
1034 }
1035
1036 INIT_GMP_RETVAL(gmpnumber);
1037 if (convert_to_gmp(gmpnumber, number_arg, base) == FAILURE) {
1038 zval_ptr_dtor(return_value);
1039 RETURN_FALSE;
1040 }
1041 }
1042 /* }}} */
1043
gmp_import_export_validate(zend_long size,zend_long options,int * order,int * endian)1044 int gmp_import_export_validate(zend_long size, zend_long options, int *order, int *endian)
1045 {
1046 if (size < 1) {
1047 php_error_docref(NULL, E_WARNING,
1048 "Word size must be positive, " ZEND_LONG_FMT " given", size);
1049 return FAILURE;
1050 }
1051
1052 switch (options & (GMP_LSW_FIRST | GMP_MSW_FIRST)) {
1053 case GMP_LSW_FIRST:
1054 *order = -1;
1055 break;
1056 case GMP_MSW_FIRST:
1057 case 0: /* default */
1058 *order = 1;
1059 break;
1060 default:
1061 php_error_docref(NULL, E_WARNING,
1062 "Invalid options: Conflicting word orders");
1063 return FAILURE;
1064 }
1065
1066 switch (options & (GMP_LITTLE_ENDIAN | GMP_BIG_ENDIAN | GMP_NATIVE_ENDIAN)) {
1067 case GMP_LITTLE_ENDIAN:
1068 *endian = -1;
1069 break;
1070 case GMP_BIG_ENDIAN:
1071 *endian = 1;
1072 break;
1073 case GMP_NATIVE_ENDIAN:
1074 case 0: /* default */
1075 *endian = 0;
1076 break;
1077 default:
1078 php_error_docref(NULL, E_WARNING,
1079 "Invalid options: Conflicting word endianness");
1080 return FAILURE;
1081 }
1082
1083 return SUCCESS;
1084 }
1085
1086 /* {{{ proto GMP gmp_import(string data [, int word_size = 1, int options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN])
1087 Imports a GMP number from a binary string */
ZEND_FUNCTION(gmp_import)1088 ZEND_FUNCTION(gmp_import)
1089 {
1090 char *data;
1091 size_t data_len;
1092 zend_long size = 1;
1093 zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
1094 int order, endian;
1095 mpz_ptr gmpnumber;
1096
1097 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &data, &data_len, &size, &options) == FAILURE) {
1098 return;
1099 }
1100
1101 if (gmp_import_export_validate(size, options, &order, &endian) == FAILURE) {
1102 RETURN_FALSE;
1103 }
1104
1105 if ((data_len % size) != 0) {
1106 php_error_docref(NULL, E_WARNING,
1107 "Input length must be a multiple of word size");
1108 RETURN_FALSE;
1109 }
1110
1111 INIT_GMP_RETVAL(gmpnumber);
1112
1113 mpz_import(gmpnumber, data_len / size, order, size, endian, 0, data);
1114 }
1115 /* }}} */
1116
1117 /* {{{ proto string gmp_export(GMP gmpnumber [, int word_size = 1, int options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN])
1118 Exports a GMP number to a binary string */
ZEND_FUNCTION(gmp_export)1119 ZEND_FUNCTION(gmp_export)
1120 {
1121 zval *gmpnumber_arg;
1122 zend_long size = 1;
1123 zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
1124 int order, endian;
1125 mpz_ptr gmpnumber;
1126 gmp_temp_t temp_a;
1127
1128 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|ll", &gmpnumber_arg, &size, &options) == FAILURE) {
1129 return;
1130 }
1131
1132 if (gmp_import_export_validate(size, options, &order, &endian) == FAILURE) {
1133 RETURN_FALSE;
1134 }
1135
1136 FETCH_GMP_ZVAL(gmpnumber, gmpnumber_arg, temp_a);
1137
1138 if (mpz_sgn(gmpnumber) == 0) {
1139 RETVAL_EMPTY_STRING();
1140 } else {
1141 size_t bits_per_word = size * 8;
1142 size_t count = (mpz_sizeinbase(gmpnumber, 2) + bits_per_word - 1) / bits_per_word;
1143
1144 zend_string *out_string = zend_string_safe_alloc(count, size, 0, 0);
1145 mpz_export(ZSTR_VAL(out_string), NULL, order, size, endian, 0, gmpnumber);
1146 ZSTR_VAL(out_string)[ZSTR_LEN(out_string)] = '\0';
1147
1148 RETVAL_NEW_STR(out_string);
1149 }
1150
1151 FREE_GMP_TEMP(temp_a);
1152 }
1153 /* }}} */
1154
1155 /* {{{ proto int gmp_intval(mixed gmpnumber)
1156 Gets signed long value of GMP number */
ZEND_FUNCTION(gmp_intval)1157 ZEND_FUNCTION(gmp_intval)
1158 {
1159 zval *gmpnumber_arg;
1160
1161 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &gmpnumber_arg) == FAILURE){
1162 return;
1163 }
1164
1165 if (IS_GMP(gmpnumber_arg)) {
1166 RETVAL_LONG(mpz_get_si(GET_GMP_FROM_ZVAL(gmpnumber_arg)));
1167 } else {
1168 RETVAL_LONG(zval_get_long(gmpnumber_arg));
1169 }
1170 }
1171 /* }}} */
1172
1173 /* {{{ proto string gmp_strval(mixed gmpnumber [, int base])
1174 Gets string representation of GMP number */
ZEND_FUNCTION(gmp_strval)1175 ZEND_FUNCTION(gmp_strval)
1176 {
1177 zval *gmpnumber_arg;
1178 zend_long base = 10;
1179 mpz_ptr gmpnum;
1180 gmp_temp_t temp_a;
1181
1182 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &base) == FAILURE) {
1183 return;
1184 }
1185
1186 /* Although the maximum base in general in GMP is 62, mpz_get_str()
1187 * is explicitly limited to -36 when dealing with negative bases. */
1188 if ((base < 2 && base > -2) || base > GMP_MAX_BASE || base < -36) {
1189 php_error_docref(NULL, E_WARNING, "Bad base for conversion: " ZEND_LONG_FMT " (should be between 2 and %d or -2 and -36)", base, GMP_MAX_BASE);
1190 RETURN_FALSE;
1191 }
1192
1193 FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a);
1194
1195 gmp_strval(return_value, gmpnum, (int)base);
1196
1197 FREE_GMP_TEMP(temp_a);
1198 }
1199 /* }}} */
1200
1201 /* {{{ proto GMP gmp_add(mixed a, mixed b)
1202 Add a and b */
ZEND_FUNCTION(gmp_add)1203 ZEND_FUNCTION(gmp_add)
1204 {
1205 gmp_binary_ui_op(mpz_add, mpz_add_ui);
1206 }
1207 /* }}} */
1208
1209 /* {{{ proto GMP gmp_sub(mixed a, mixed b)
1210 Subtract b from a */
ZEND_FUNCTION(gmp_sub)1211 ZEND_FUNCTION(gmp_sub)
1212 {
1213 gmp_binary_ui_op(mpz_sub, mpz_sub_ui);
1214 }
1215 /* }}} */
1216
1217 /* {{{ proto GMP gmp_mul(mixed a, mixed b)
1218 Multiply a and b */
ZEND_FUNCTION(gmp_mul)1219 ZEND_FUNCTION(gmp_mul)
1220 {
1221 gmp_binary_ui_op(mpz_mul, mpz_mul_ui);
1222 }
1223 /* }}} */
1224
1225 /* {{{ proto array gmp_div_qr(mixed a, mixed b [, int round])
1226 Divide a by b, returns quotient and reminder */
ZEND_FUNCTION(gmp_div_qr)1227 ZEND_FUNCTION(gmp_div_qr)
1228 {
1229 zval *a_arg, *b_arg;
1230 zend_long round = GMP_ROUND_ZERO;
1231
1232 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1233 return;
1234 }
1235
1236 switch (round) {
1237 case GMP_ROUND_ZERO:
1238 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_tdiv_qr, (gmp_binary_ui_op2_t) mpz_tdiv_qr_ui, 1);
1239 break;
1240 case GMP_ROUND_PLUSINF:
1241 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_cdiv_qr, (gmp_binary_ui_op2_t) mpz_cdiv_qr_ui, 1);
1242 break;
1243 case GMP_ROUND_MINUSINF:
1244 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_fdiv_qr, (gmp_binary_ui_op2_t) mpz_fdiv_qr_ui, 1);
1245 break;
1246 default:
1247 php_error_docref(NULL, E_WARNING, "Invalid rounding mode");
1248 RETURN_FALSE;
1249 }
1250 }
1251 /* }}} */
1252
1253 /* {{{ proto GMP gmp_div_r(mixed a, mixed b [, int round])
1254 Divide a by b, returns reminder only */
ZEND_FUNCTION(gmp_div_r)1255 ZEND_FUNCTION(gmp_div_r)
1256 {
1257 zval *a_arg, *b_arg;
1258 zend_long round = GMP_ROUND_ZERO;
1259
1260 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1261 return;
1262 }
1263
1264 switch (round) {
1265 case GMP_ROUND_ZERO:
1266 gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_tdiv_r, (gmp_binary_ui_op_t) mpz_tdiv_r_ui, 1);
1267 break;
1268 case GMP_ROUND_PLUSINF:
1269 gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_cdiv_r, (gmp_binary_ui_op_t) mpz_cdiv_r_ui, 1);
1270 break;
1271 case GMP_ROUND_MINUSINF:
1272 gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_fdiv_r, (gmp_binary_ui_op_t) mpz_fdiv_r_ui, 1);
1273 break;
1274 default:
1275 php_error_docref(NULL, E_WARNING, "Invalid rounding mode");
1276 RETURN_FALSE;
1277 }
1278 }
1279 /* }}} */
1280
1281 /* {{{ proto GMP gmp_div_q(mixed a, mixed b [, int round])
1282 Divide a by b, returns quotient only */
ZEND_FUNCTION(gmp_div_q)1283 ZEND_FUNCTION(gmp_div_q)
1284 {
1285 zval *a_arg, *b_arg;
1286 zend_long round = GMP_ROUND_ZERO;
1287
1288 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1289 return;
1290 }
1291
1292 switch (round) {
1293 case GMP_ROUND_ZERO:
1294 gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_tdiv_q, (gmp_binary_ui_op_t) mpz_tdiv_q_ui, 1);
1295 break;
1296 case GMP_ROUND_PLUSINF:
1297 gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_cdiv_q, (gmp_binary_ui_op_t) mpz_cdiv_q_ui, 1);
1298 break;
1299 case GMP_ROUND_MINUSINF:
1300 gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_fdiv_q, (gmp_binary_ui_op_t) mpz_fdiv_q_ui, 1);
1301 break;
1302 default:
1303 php_error_docref(NULL, E_WARNING, "Invalid rounding mode");
1304 RETURN_FALSE;
1305 }
1306
1307 }
1308 /* }}} */
1309
1310 /* {{{ proto GMP gmp_mod(mixed a, mixed b)
1311 Computes a modulo b */
ZEND_FUNCTION(gmp_mod)1312 ZEND_FUNCTION(gmp_mod)
1313 {
1314 gmp_binary_ui_op_no_zero(mpz_mod, (gmp_binary_ui_op_t) mpz_mod_ui);
1315 }
1316 /* }}} */
1317
1318 /* {{{ proto GMP gmp_divexact(mixed a, mixed b)
1319 Divide a by b using exact division algorithm */
ZEND_FUNCTION(gmp_divexact)1320 ZEND_FUNCTION(gmp_divexact)
1321 {
1322 gmp_binary_ui_op_no_zero(mpz_divexact, NULL);
1323 }
1324 /* }}} */
1325
1326 /* {{{ proto GMP gmp_neg(mixed a)
1327 Negates a number */
ZEND_FUNCTION(gmp_neg)1328 ZEND_FUNCTION(gmp_neg)
1329 {
1330 gmp_unary_op(mpz_neg);
1331 }
1332 /* }}} */
1333
1334 /* {{{ proto GMP gmp_abs(mixed a)
1335 Calculates absolute value */
ZEND_FUNCTION(gmp_abs)1336 ZEND_FUNCTION(gmp_abs)
1337 {
1338 gmp_unary_op(mpz_abs);
1339 }
1340 /* }}} */
1341
1342 /* {{{ proto GMP gmp_fact(int a)
1343 Calculates factorial function */
ZEND_FUNCTION(gmp_fact)1344 ZEND_FUNCTION(gmp_fact)
1345 {
1346 zval *a_arg;
1347
1348 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1349 return;
1350 }
1351
1352 if (IS_GMP(a_arg)) {
1353 mpz_ptr gmpnum_tmp = GET_GMP_FROM_ZVAL(a_arg);
1354 if (mpz_sgn(gmpnum_tmp) < 0) {
1355 php_error_docref(NULL, E_WARNING, "Number has to be greater than or equal to 0");
1356 RETURN_FALSE;
1357 }
1358 } else {
1359 /* Use convert_to_number first to detect getting non-integer */
1360 convert_scalar_to_number(a_arg);
1361 if (Z_TYPE_P(a_arg) != IS_LONG) {
1362 convert_to_long(a_arg);
1363 if (Z_LVAL_P(a_arg) >= 0) {
1364 /* Only warn if we'll make it past the non-negative check */
1365 php_error_docref(NULL, E_WARNING, "Number has to be an integer");
1366 }
1367 }
1368 if (Z_LVAL_P(a_arg) < 0) {
1369 php_error_docref(NULL, E_WARNING, "Number has to be greater than or equal to 0");
1370 RETURN_FALSE;
1371 }
1372 }
1373
1374 gmp_zval_unary_ui_op(return_value, a_arg, mpz_fac_ui);
1375 }
1376 /* }}} */
1377
1378 /* {{{ proto GMP gmp_binomial(mixed n, int k)
1379 * Calculates binomial coefficient */
ZEND_FUNCTION(gmp_binomial)1380 ZEND_FUNCTION(gmp_binomial)
1381 {
1382 zval *n_arg;
1383 zend_long k;
1384 mpz_ptr gmpnum_result;
1385
1386 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &n_arg, &k) == FAILURE) {
1387 return;
1388 }
1389
1390 if (k < 0) {
1391 php_error_docref(NULL, E_WARNING, "k cannot be negative");
1392 RETURN_FALSE;
1393 }
1394
1395 INIT_GMP_RETVAL(gmpnum_result);
1396 if (Z_TYPE_P(n_arg) == IS_LONG && Z_LVAL_P(n_arg) >= 0) {
1397 mpz_bin_uiui(gmpnum_result, (gmp_ulong) Z_LVAL_P(n_arg), (gmp_ulong) k);
1398 } else {
1399 mpz_ptr gmpnum_n;
1400 gmp_temp_t temp_n;
1401 FETCH_GMP_ZVAL(gmpnum_n, n_arg, temp_n);
1402 mpz_bin_ui(gmpnum_result, gmpnum_n, (gmp_ulong) k);
1403 FREE_GMP_TEMP(temp_n);
1404 }
1405 }
1406 /* }}} */
1407
1408 /* {{{ proto GMP gmp_pow(mixed base, int exp)
1409 Raise base to power exp */
ZEND_FUNCTION(gmp_pow)1410 ZEND_FUNCTION(gmp_pow)
1411 {
1412 zval *base_arg;
1413 mpz_ptr gmpnum_result;
1414 gmp_temp_t temp_base;
1415 zend_long exp;
1416
1417 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &base_arg, &exp) == FAILURE) {
1418 return;
1419 }
1420
1421 if (exp < 0) {
1422 php_error_docref(NULL, E_WARNING, "Negative exponent not supported");
1423 RETURN_FALSE;
1424 }
1425
1426 if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) {
1427 INIT_GMP_RETVAL(gmpnum_result);
1428 mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
1429 } else {
1430 mpz_ptr gmpnum_base;
1431 FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base);
1432 INIT_GMP_RETVAL(gmpnum_result);
1433 mpz_pow_ui(gmpnum_result, gmpnum_base, exp);
1434 FREE_GMP_TEMP(temp_base);
1435 }
1436 }
1437 /* }}} */
1438
1439 /* {{{ proto GMP gmp_powm(mixed base, mixed exp, mixed mod)
1440 Raise base to power exp and take result modulo mod */
ZEND_FUNCTION(gmp_powm)1441 ZEND_FUNCTION(gmp_powm)
1442 {
1443 zval *base_arg, *exp_arg, *mod_arg;
1444 mpz_ptr gmpnum_base, gmpnum_exp, gmpnum_mod, gmpnum_result;
1445 int use_ui = 0;
1446 gmp_temp_t temp_base, temp_exp, temp_mod;
1447
1448 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz", &base_arg, &exp_arg, &mod_arg) == FAILURE){
1449 return;
1450 }
1451
1452 FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base);
1453
1454 if (Z_TYPE_P(exp_arg) == IS_LONG && Z_LVAL_P(exp_arg) >= 0) {
1455 use_ui = 1;
1456 temp_exp.is_used = 0;
1457 } else {
1458 FETCH_GMP_ZVAL_DEP(gmpnum_exp, exp_arg, temp_exp, temp_base);
1459 if (mpz_sgn(gmpnum_exp) < 0) {
1460 php_error_docref(NULL, E_WARNING, "Second parameter cannot be less than 0");
1461 FREE_GMP_TEMP(temp_base);
1462 FREE_GMP_TEMP(temp_exp);
1463 RETURN_FALSE;
1464 }
1465 }
1466 FETCH_GMP_ZVAL_DEP_DEP(gmpnum_mod, mod_arg, temp_mod, temp_exp, temp_base);
1467
1468 if (!mpz_cmp_ui(gmpnum_mod, 0)) {
1469 php_error_docref(NULL, E_WARNING, "Modulus may not be zero");
1470 FREE_GMP_TEMP(temp_base);
1471 FREE_GMP_TEMP(temp_exp);
1472 FREE_GMP_TEMP(temp_mod);
1473 RETURN_FALSE;
1474 }
1475
1476 INIT_GMP_RETVAL(gmpnum_result);
1477 if (use_ui) {
1478 mpz_powm_ui(gmpnum_result, gmpnum_base, (zend_ulong) Z_LVAL_P(exp_arg), gmpnum_mod);
1479 } else {
1480 mpz_powm(gmpnum_result, gmpnum_base, gmpnum_exp, gmpnum_mod);
1481 FREE_GMP_TEMP(temp_exp);
1482 }
1483
1484 FREE_GMP_TEMP(temp_base);
1485 FREE_GMP_TEMP(temp_mod);
1486 }
1487 /* }}} */
1488
1489 /* {{{ proto GMP gmp_sqrt(mixed a)
1490 Takes integer part of square root of a */
ZEND_FUNCTION(gmp_sqrt)1491 ZEND_FUNCTION(gmp_sqrt)
1492 {
1493 zval *a_arg;
1494 mpz_ptr gmpnum_a, gmpnum_result;
1495 gmp_temp_t temp_a;
1496
1497 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1498 return;
1499 }
1500
1501 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1502
1503 if (mpz_sgn(gmpnum_a) < 0) {
1504 php_error_docref(NULL, E_WARNING, "Number has to be greater than or equal to 0");
1505 FREE_GMP_TEMP(temp_a);
1506 RETURN_FALSE;
1507 }
1508
1509 INIT_GMP_RETVAL(gmpnum_result);
1510 mpz_sqrt(gmpnum_result, gmpnum_a);
1511 FREE_GMP_TEMP(temp_a);
1512 }
1513 /* }}} */
1514
1515 /* {{{ proto array gmp_sqrtrem(mixed a)
1516 Square root with remainder */
ZEND_FUNCTION(gmp_sqrtrem)1517 ZEND_FUNCTION(gmp_sqrtrem)
1518 {
1519 zval *a_arg;
1520 mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
1521 gmp_temp_t temp_a;
1522 zval result1, result2;
1523
1524 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1525 return;
1526 }
1527
1528 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1529
1530 if (mpz_sgn(gmpnum_a) < 0) {
1531 php_error_docref(NULL, E_WARNING, "Number has to be greater than or equal to 0");
1532 FREE_GMP_TEMP(temp_a);
1533 RETURN_FALSE;
1534 }
1535
1536 gmp_create(&result1, &gmpnum_result1);
1537 gmp_create(&result2, &gmpnum_result2);
1538
1539 array_init(return_value);
1540 add_next_index_zval(return_value, &result1);
1541 add_next_index_zval(return_value, &result2);
1542
1543 mpz_sqrtrem(gmpnum_result1, gmpnum_result2, gmpnum_a);
1544 FREE_GMP_TEMP(temp_a);
1545 }
1546 /* }}} */
1547
1548 /* {{{ proto GMP gmp_root(mixed a, int nth)
1549 Takes integer part of nth root */
ZEND_FUNCTION(gmp_root)1550 ZEND_FUNCTION(gmp_root)
1551 {
1552 zval *a_arg;
1553 zend_long nth;
1554 mpz_ptr gmpnum_a, gmpnum_result;
1555 gmp_temp_t temp_a;
1556
1557 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
1558 return;
1559 }
1560
1561 if (nth <= 0) {
1562 php_error_docref(NULL, E_WARNING, "The root must be positive");
1563 RETURN_FALSE;
1564 }
1565
1566 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1567
1568 if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
1569 php_error_docref(NULL, E_WARNING, "Can't take even root of negative number");
1570 FREE_GMP_TEMP(temp_a);
1571 RETURN_FALSE;
1572 }
1573
1574 INIT_GMP_RETVAL(gmpnum_result);
1575 mpz_root(gmpnum_result, gmpnum_a, (gmp_ulong) nth);
1576 FREE_GMP_TEMP(temp_a);
1577 }
1578 /* }}} */
1579
1580 /* {{{ proto GMP gmp_rootrem(mixed a, int nth)
1581 Calculates integer part of nth root and remainder */
ZEND_FUNCTION(gmp_rootrem)1582 ZEND_FUNCTION(gmp_rootrem)
1583 {
1584 zval *a_arg;
1585 zend_long nth;
1586 mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
1587 gmp_temp_t temp_a;
1588 zval result1, result2;
1589
1590 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
1591 return;
1592 }
1593
1594 if (nth <= 0) {
1595 php_error_docref(NULL, E_WARNING, "The root must be positive");
1596 RETURN_FALSE;
1597 }
1598
1599 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1600
1601 if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
1602 php_error_docref(NULL, E_WARNING, "Can't take even root of negative number");
1603 FREE_GMP_TEMP(temp_a);
1604 RETURN_FALSE;
1605 }
1606
1607 gmp_create(&result1, &gmpnum_result1);
1608 gmp_create(&result2, &gmpnum_result2);
1609
1610 array_init(return_value);
1611 add_next_index_zval(return_value, &result1);
1612 add_next_index_zval(return_value, &result2);
1613
1614 #if GMP_51_OR_NEWER
1615 /* mpz_rootrem() is supported since GMP 4.2, but buggy wrt odd roots
1616 * of negative numbers */
1617 mpz_rootrem(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) nth);
1618 #else
1619 mpz_root(gmpnum_result1, gmpnum_a, (gmp_ulong) nth);
1620 mpz_pow_ui(gmpnum_result2, gmpnum_result1, (gmp_ulong) nth);
1621 mpz_sub(gmpnum_result2, gmpnum_a, gmpnum_result2);
1622 #endif
1623
1624 FREE_GMP_TEMP(temp_a);
1625 }
1626 /* }}} */
1627
1628 /* {{{ proto bool gmp_perfect_square(mixed a)
1629 Checks if a is an exact square */
ZEND_FUNCTION(gmp_perfect_square)1630 ZEND_FUNCTION(gmp_perfect_square)
1631 {
1632 zval *a_arg;
1633 mpz_ptr gmpnum_a;
1634 gmp_temp_t temp_a;
1635
1636 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1637 return;
1638 }
1639
1640 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1641
1642 RETVAL_BOOL((mpz_perfect_square_p(gmpnum_a) != 0));
1643 FREE_GMP_TEMP(temp_a);
1644 }
1645 /* }}} */
1646
1647 /* {{{ proto bool gmp_perfect_power(mixed a)
1648 Checks if a is a perfect power */
ZEND_FUNCTION(gmp_perfect_power)1649 ZEND_FUNCTION(gmp_perfect_power)
1650 {
1651 zval *a_arg;
1652 mpz_ptr gmpnum_a;
1653 gmp_temp_t temp_a;
1654
1655 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1656 return;
1657 }
1658
1659 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1660
1661 RETVAL_BOOL((mpz_perfect_power_p(gmpnum_a) != 0));
1662 FREE_GMP_TEMP(temp_a);
1663 }
1664 /* }}} */
1665
1666 /* {{{ proto int gmp_prob_prime(mixed a[, int reps])
1667 Checks if a is "probably prime" */
ZEND_FUNCTION(gmp_prob_prime)1668 ZEND_FUNCTION(gmp_prob_prime)
1669 {
1670 zval *gmpnumber_arg;
1671 mpz_ptr gmpnum_a;
1672 zend_long reps = 10;
1673 gmp_temp_t temp_a;
1674
1675 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &reps) == FAILURE) {
1676 return;
1677 }
1678
1679 FETCH_GMP_ZVAL(gmpnum_a, gmpnumber_arg, temp_a);
1680
1681 RETVAL_LONG(mpz_probab_prime_p(gmpnum_a, (int)reps));
1682 FREE_GMP_TEMP(temp_a);
1683 }
1684 /* }}} */
1685
1686 /* {{{ proto GMP gmp_gcd(mixed a, mixed b)
1687 Computes greatest common denominator (gcd) of a and b */
ZEND_FUNCTION(gmp_gcd)1688 ZEND_FUNCTION(gmp_gcd)
1689 {
1690 gmp_binary_ui_op(mpz_gcd, (gmp_binary_ui_op_t) mpz_gcd_ui);
1691 }
1692 /* }}} */
1693
1694 /* {{{ proto GMP gmp_lcm(mixed a, mixed b)
1695 Computes least common multiple (lcm) of a and b */
ZEND_FUNCTION(gmp_lcm)1696 ZEND_FUNCTION(gmp_lcm)
1697 {
1698 gmp_binary_ui_op(mpz_lcm, (gmp_binary_ui_op_t) mpz_lcm_ui);
1699 }
1700 /* }}} */
1701
1702 /* {{{ proto array gmp_gcdext(mixed a, mixed b)
1703 Computes G, S, and T, such that AS + BT = G = `gcd' (A, B) */
ZEND_FUNCTION(gmp_gcdext)1704 ZEND_FUNCTION(gmp_gcdext)
1705 {
1706 zval *a_arg, *b_arg;
1707 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_t, gmpnum_s, gmpnum_g;
1708 gmp_temp_t temp_a, temp_b;
1709 zval result_g, result_s, result_t;
1710
1711 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1712 return;
1713 }
1714
1715 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1716 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a);
1717
1718 gmp_create(&result_g, &gmpnum_g);
1719 gmp_create(&result_s, &gmpnum_s);
1720 gmp_create(&result_t, &gmpnum_t);
1721
1722 array_init(return_value);
1723 add_assoc_zval(return_value, "g", &result_g);
1724 add_assoc_zval(return_value, "s", &result_s);
1725 add_assoc_zval(return_value, "t", &result_t);
1726
1727 mpz_gcdext(gmpnum_g, gmpnum_s, gmpnum_t, gmpnum_a, gmpnum_b);
1728 FREE_GMP_TEMP(temp_a);
1729 FREE_GMP_TEMP(temp_b);
1730 }
1731 /* }}} */
1732
1733 /* {{{ proto GMP gmp_invert(mixed a, mixed b)
1734 Computes the inverse of a modulo b */
ZEND_FUNCTION(gmp_invert)1735 ZEND_FUNCTION(gmp_invert)
1736 {
1737 zval *a_arg, *b_arg;
1738 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
1739 gmp_temp_t temp_a, temp_b;
1740 int res;
1741
1742 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1743 return;
1744 }
1745
1746 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1747 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a);
1748
1749 INIT_GMP_RETVAL(gmpnum_result);
1750 res = mpz_invert(gmpnum_result, gmpnum_a, gmpnum_b);
1751 FREE_GMP_TEMP(temp_a);
1752 FREE_GMP_TEMP(temp_b);
1753 if (!res) {
1754 zval_ptr_dtor(return_value);
1755 RETURN_FALSE;
1756 }
1757 }
1758 /* }}} */
1759
1760 /* {{{ proto int gmp_jacobi(mixed a, mixed b)
1761 Computes Jacobi symbol */
ZEND_FUNCTION(gmp_jacobi)1762 ZEND_FUNCTION(gmp_jacobi)
1763 {
1764 gmp_binary_opl(mpz_jacobi);
1765 }
1766 /* }}} */
1767
1768 /* {{{ proto int gmp_legendre(mixed a, mixed b)
1769 Computes Legendre symbol */
ZEND_FUNCTION(gmp_legendre)1770 ZEND_FUNCTION(gmp_legendre)
1771 {
1772 gmp_binary_opl(mpz_legendre);
1773 }
1774 /* }}} */
1775
1776 /* {{{ proto int gmp_kronecker(mixed a, mixed b)
1777 Computes the Kronecker symbol */
ZEND_FUNCTION(gmp_kronecker)1778 ZEND_FUNCTION(gmp_kronecker)
1779 {
1780 zval *a_arg, *b_arg;
1781 mpz_ptr gmpnum_a, gmpnum_b;
1782 gmp_temp_t temp_a, temp_b;
1783 zend_bool use_a_si = 0, use_b_si = 0;
1784 int result;
1785
1786 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1787 return;
1788 }
1789
1790 if (Z_TYPE_P(a_arg) == IS_LONG && Z_TYPE_P(b_arg) != IS_LONG) {
1791 use_a_si = 1;
1792 temp_a.is_used = 0;
1793 } else {
1794 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1795 }
1796
1797 if (Z_TYPE_P(b_arg) == IS_LONG) {
1798 use_b_si = 1;
1799 temp_b.is_used = 0;
1800 } else {
1801 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a);
1802 }
1803
1804 if (use_a_si) {
1805 ZEND_ASSERT(use_b_si == 0);
1806 result = mpz_si_kronecker((gmp_long) Z_LVAL_P(a_arg), gmpnum_b);
1807 } else if (use_b_si) {
1808 result = mpz_kronecker_si(gmpnum_a, (gmp_long) Z_LVAL_P(b_arg));
1809 } else {
1810 result = mpz_kronecker(gmpnum_a, gmpnum_b);
1811 }
1812
1813 FREE_GMP_TEMP(temp_a);
1814 FREE_GMP_TEMP(temp_b);
1815
1816 RETURN_LONG(result);
1817 }
1818 /* }}} */
1819
1820 /* {{{ proto int gmp_cmp(mixed a, mixed b)
1821 Compares two numbers */
ZEND_FUNCTION(gmp_cmp)1822 ZEND_FUNCTION(gmp_cmp)
1823 {
1824 zval *a_arg, *b_arg;
1825
1826 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1827 return;
1828 }
1829
1830 gmp_cmp(return_value, a_arg, b_arg);
1831 }
1832 /* }}} */
1833
1834 /* {{{ proto int gmp_sign(mixed a)
1835 Gets the sign of the number */
ZEND_FUNCTION(gmp_sign)1836 ZEND_FUNCTION(gmp_sign)
1837 {
1838 /* Can't use gmp_unary_opl here, because mpz_sgn is a macro */
1839 zval *a_arg;
1840 mpz_ptr gmpnum_a;
1841 gmp_temp_t temp_a;
1842
1843 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1844 return;
1845 }
1846
1847 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
1848
1849 RETVAL_LONG(mpz_sgn(gmpnum_a));
1850 FREE_GMP_TEMP(temp_a);
1851 }
1852 /* }}} */
1853
gmp_init_random(void)1854 static void gmp_init_random(void)
1855 {
1856 if (!GMPG(rand_initialized)) {
1857 /* Initialize */
1858 gmp_randinit_mt(GMPG(rand_state));
1859 /* Seed */
1860 gmp_randseed_ui(GMPG(rand_state), GENERATE_SEED());
1861
1862 GMPG(rand_initialized) = 1;
1863 }
1864 }
1865
1866 /* {{{ proto GMP gmp_random([int limiter])
1867 Gets random number */
ZEND_FUNCTION(gmp_random)1868 ZEND_FUNCTION(gmp_random)
1869 {
1870 zend_long limiter = 20;
1871 mpz_ptr gmpnum_result;
1872
1873 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &limiter) == FAILURE) {
1874 return;
1875 }
1876
1877 INIT_GMP_RETVAL(gmpnum_result);
1878 gmp_init_random();
1879
1880 #ifdef GMP_LIMB_BITS
1881 mpz_urandomb(gmpnum_result, GMPG(rand_state), GMP_ABS (limiter) * GMP_LIMB_BITS);
1882 #else
1883 mpz_urandomb(gmpnum_result, GMPG(rand_state), GMP_ABS (limiter) * __GMP_BITS_PER_MP_LIMB);
1884 #endif
1885 }
1886 /* }}} */
1887
1888 /* {{{ proto GMP gmp_random_seed(mixed seed)
1889 Seed the RNG */
ZEND_FUNCTION(gmp_random_seed)1890 ZEND_FUNCTION(gmp_random_seed)
1891 {
1892 zval *seed;
1893 gmp_temp_t temp_a;
1894
1895 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &seed) == FAILURE) {
1896 return;
1897 }
1898
1899 gmp_init_random();
1900
1901 if (Z_TYPE_P(seed) == IS_LONG && Z_LVAL_P(seed) >= 0) {
1902 gmp_randseed_ui(GMPG(rand_state), Z_LVAL_P(seed));
1903 }
1904 else {
1905 mpz_ptr gmpnum_seed;
1906
1907 FETCH_GMP_ZVAL(gmpnum_seed, seed, temp_a);
1908
1909 gmp_randseed(GMPG(rand_state), gmpnum_seed);
1910
1911 FREE_GMP_TEMP(temp_a);
1912 }
1913 }
1914 /* }}} */
1915
1916 /* {{{ proto GMP gmp_random_bits(int bits)
1917 Gets a random number in the range 0 to (2 ** n) - 1 */
ZEND_FUNCTION(gmp_random_bits)1918 ZEND_FUNCTION(gmp_random_bits)
1919 {
1920 zend_long bits;
1921 mpz_ptr gmpnum_result;
1922
1923 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &bits) == FAILURE) {
1924 return;
1925 }
1926
1927 if (bits <= 0) {
1928 php_error_docref(NULL, E_WARNING, "The number of bits must be positive");
1929 RETURN_FALSE;
1930 }
1931
1932 INIT_GMP_RETVAL(gmpnum_result);
1933 gmp_init_random();
1934
1935 mpz_urandomb(gmpnum_result, GMPG(rand_state), bits);
1936 }
1937 /* }}} */
1938
1939 /* {{{ proto GMP gmp_random_range(mixed min, mixed max)
1940 Gets a random number in the range min to max */
ZEND_FUNCTION(gmp_random_range)1941 ZEND_FUNCTION(gmp_random_range)
1942 {
1943 zval *min_arg, *max_arg;
1944 mpz_ptr gmpnum_max, gmpnum_result;
1945 mpz_t gmpnum_range;
1946 gmp_temp_t temp_a, temp_b;
1947
1948 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &min_arg, &max_arg) == FAILURE) {
1949 return;
1950 }
1951
1952 gmp_init_random();
1953
1954 FETCH_GMP_ZVAL(gmpnum_max, max_arg, temp_a);
1955
1956 if (Z_TYPE_P(min_arg) == IS_LONG && Z_LVAL_P(min_arg) >= 0) {
1957 if (mpz_cmp_ui(gmpnum_max, Z_LVAL_P(min_arg)) <= 0) {
1958 FREE_GMP_TEMP(temp_a);
1959 php_error_docref(NULL, E_WARNING, "The minimum value must be less than the maximum value");
1960 RETURN_FALSE;
1961 }
1962
1963 INIT_GMP_RETVAL(gmpnum_result);
1964 mpz_init(gmpnum_range);
1965
1966 if (Z_LVAL_P(min_arg) != 0) {
1967 mpz_sub_ui(gmpnum_range, gmpnum_max, Z_LVAL_P(min_arg) - 1);
1968 } else {
1969 mpz_add_ui(gmpnum_range, gmpnum_max, 1);
1970 }
1971
1972 mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1973
1974 if (Z_LVAL_P(min_arg) != 0) {
1975 mpz_add_ui(gmpnum_result, gmpnum_result, Z_LVAL_P(min_arg));
1976 }
1977
1978 mpz_clear(gmpnum_range);
1979 FREE_GMP_TEMP(temp_a);
1980 } else {
1981 mpz_ptr gmpnum_min;
1982
1983 FETCH_GMP_ZVAL_DEP(gmpnum_min, min_arg, temp_b, temp_a);
1984
1985 if (mpz_cmp(gmpnum_max, gmpnum_min) <= 0) {
1986 FREE_GMP_TEMP(temp_b);
1987 FREE_GMP_TEMP(temp_a);
1988 php_error_docref(NULL, E_WARNING, "The minimum value must be less than the maximum value");
1989 RETURN_FALSE;
1990 }
1991
1992 INIT_GMP_RETVAL(gmpnum_result);
1993 mpz_init(gmpnum_range);
1994
1995 mpz_sub(gmpnum_range, gmpnum_max, gmpnum_min);
1996 mpz_add_ui(gmpnum_range, gmpnum_range, 1);
1997 mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1998 mpz_add(gmpnum_result, gmpnum_result, gmpnum_min);
1999
2000 mpz_clear(gmpnum_range);
2001 FREE_GMP_TEMP(temp_b);
2002 FREE_GMP_TEMP(temp_a);
2003 }
2004 }
2005 /* }}} */
2006
2007 /* {{{ proto GMP gmp_and(mixed a, mixed b)
2008 Calculates logical AND of a and b */
ZEND_FUNCTION(gmp_and)2009 ZEND_FUNCTION(gmp_and)
2010 {
2011 gmp_binary_op(mpz_and);
2012 }
2013 /* }}} */
2014
2015 /* {{{ proto GMP gmp_or(mixed a, mixed b)
2016 Calculates logical OR of a and b */
ZEND_FUNCTION(gmp_or)2017 ZEND_FUNCTION(gmp_or)
2018 {
2019 gmp_binary_op(mpz_ior);
2020 }
2021 /* }}} */
2022
2023 /* {{{ proto GMP gmp_com(mixed a)
2024 Calculates one's complement of a */
ZEND_FUNCTION(gmp_com)2025 ZEND_FUNCTION(gmp_com)
2026 {
2027 gmp_unary_op(mpz_com);
2028 }
2029 /* }}} */
2030
2031 /* {{{ proto GMP gmp_nextprime(mixed a)
2032 Finds next prime of a */
ZEND_FUNCTION(gmp_nextprime)2033 ZEND_FUNCTION(gmp_nextprime)
2034 {
2035 gmp_unary_op(mpz_nextprime);
2036 }
2037 /* }}} */
2038
2039 /* {{{ proto GMP gmp_xor(mixed a, mixed b)
2040 Calculates logical exclusive OR of a and b */
ZEND_FUNCTION(gmp_xor)2041 ZEND_FUNCTION(gmp_xor)
2042 {
2043 gmp_binary_op(mpz_xor);
2044 }
2045 /* }}} */
2046
2047 /* {{{ proto void gmp_setbit(GMP a, int index[, bool set_clear])
2048 Sets or clear bit in a */
ZEND_FUNCTION(gmp_setbit)2049 ZEND_FUNCTION(gmp_setbit)
2050 {
2051 zval *a_arg;
2052 zend_long index;
2053 zend_bool set = 1;
2054 mpz_ptr gmpnum_a;
2055
2056 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|b", &a_arg, gmp_ce, &index, &set) == FAILURE) {
2057 return;
2058 }
2059
2060 if (index < 0) {
2061 php_error_docref(NULL, E_WARNING, "Index must be greater than or equal to zero");
2062 RETURN_FALSE;
2063 }
2064 if (index / GMP_NUMB_BITS >= INT_MAX) {
2065 php_error_docref(NULL, E_WARNING, "Index must be less than %d * %d", INT_MAX, GMP_NUMB_BITS);
2066 RETURN_FALSE;
2067 }
2068
2069 gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
2070
2071 if (set) {
2072 mpz_setbit(gmpnum_a, index);
2073 } else {
2074 mpz_clrbit(gmpnum_a, index);
2075 }
2076 }
2077 /* }}} */
2078
2079 /* {{{ proto void gmp_clrbit(GMP a, int index)
2080 Clears bit in a */
ZEND_FUNCTION(gmp_clrbit)2081 ZEND_FUNCTION(gmp_clrbit)
2082 {
2083 zval *a_arg;
2084 zend_long index;
2085 mpz_ptr gmpnum_a;
2086
2087 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &a_arg, gmp_ce, &index) == FAILURE){
2088 return;
2089 }
2090
2091 if (index < 0) {
2092 php_error_docref(NULL, E_WARNING, "Index must be greater than or equal to zero");
2093 RETURN_FALSE;
2094 }
2095
2096 gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
2097 mpz_clrbit(gmpnum_a, index);
2098 }
2099 /* }}} */
2100
2101 /* {{{ proto bool gmp_testbit(mixed a, int index)
2102 Tests if bit is set in a */
ZEND_FUNCTION(gmp_testbit)2103 ZEND_FUNCTION(gmp_testbit)
2104 {
2105 zval *a_arg;
2106 zend_long index;
2107 mpz_ptr gmpnum_a;
2108 gmp_temp_t temp_a;
2109
2110 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &index) == FAILURE){
2111 return;
2112 }
2113
2114 if (index < 0) {
2115 php_error_docref(NULL, E_WARNING, "Index must be greater than or equal to zero");
2116 RETURN_FALSE;
2117 }
2118
2119 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
2120 RETVAL_BOOL(mpz_tstbit(gmpnum_a, index));
2121 FREE_GMP_TEMP(temp_a);
2122 }
2123 /* }}} */
2124
2125 /* {{{ proto int gmp_popcount(mixed a)
2126 Calculates the population count of a */
ZEND_FUNCTION(gmp_popcount)2127 ZEND_FUNCTION(gmp_popcount)
2128 {
2129 gmp_unary_opl((gmp_unary_opl_t) mpz_popcount);
2130 }
2131 /* }}} */
2132
2133 /* {{{ proto int gmp_hamdist(mixed a, mixed b)
2134 Calculates hamming distance between a and b */
ZEND_FUNCTION(gmp_hamdist)2135 ZEND_FUNCTION(gmp_hamdist)
2136 {
2137 gmp_binary_opl((gmp_binary_opl_t) mpz_hamdist);
2138 }
2139 /* }}} */
2140
2141 /* {{{ proto int gmp_scan0(mixed a, int start)
2142 Finds first zero bit */
ZEND_FUNCTION(gmp_scan0)2143 ZEND_FUNCTION(gmp_scan0)
2144 {
2145 zval *a_arg;
2146 mpz_ptr gmpnum_a;
2147 gmp_temp_t temp_a;
2148 zend_long start;
2149
2150 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2151 return;
2152 }
2153
2154 if (start < 0) {
2155 php_error_docref(NULL, E_WARNING, "Starting index must be greater than or equal to zero");
2156 RETURN_FALSE;
2157 }
2158
2159 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
2160
2161 RETVAL_LONG(mpz_scan0(gmpnum_a, start));
2162 FREE_GMP_TEMP(temp_a);
2163 }
2164 /* }}} */
2165
2166 /* {{{ proto int gmp_scan1(mixed a, int start)
2167 Finds first non-zero bit */
ZEND_FUNCTION(gmp_scan1)2168 ZEND_FUNCTION(gmp_scan1)
2169 {
2170 zval *a_arg;
2171 mpz_ptr gmpnum_a;
2172 gmp_temp_t temp_a;
2173 zend_long start;
2174
2175 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2176 return;
2177 }
2178
2179 if (start < 0) {
2180 php_error_docref(NULL, E_WARNING, "Starting index must be greater than or equal to zero");
2181 RETURN_FALSE;
2182 }
2183
2184 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
2185
2186 RETVAL_LONG(mpz_scan1(gmpnum_a, start));
2187 FREE_GMP_TEMP(temp_a);
2188 }
2189 /* }}} */
2190