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