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