xref: /PHP-8.2/ext/gmp/gmp.c (revision 97b3b455)
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 	if (ZSTR_LEN(val) >= 2 && num_str[0] == '0') {
587 		if ((base == 0 || base == 16) && (num_str[1] == 'x' || num_str[1] == 'X')) {
588 			base = 16;
589 			skip_lead = true;
590 		} else if ((base == 0 || base == 8) && (num_str[1] == 'o' || num_str[1] == 'O')) {
591 			base = 8;
592 			skip_lead = true;
593 		} else if ((base == 0 || base == 2) && (num_str[1] == 'b' || num_str[1] == 'B')) {
594 			base = 2;
595 			skip_lead = true;
596 		}
597 	}
598 
599 	int gmp_ret = mpz_set_str(gmp_number, (skip_lead ? &num_str[2] : num_str), (int) base);
600 	if (-1 == gmp_ret) {
601 		if (arg_pos == 0) {
602 			zend_value_error("Number is not an integer string");
603 		} else {
604 			zend_argument_value_error(arg_pos, "is not an integer string");
605 		}
606 		return FAILURE;
607 	}
608 
609 	return SUCCESS;
610 }
611 
612 /* {{{ convert_to_gmp
613  * Convert zval to be gmp number */
convert_to_gmp(mpz_t gmpnumber,zval * val,zend_long base,uint32_t arg_pos)614 static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, uint32_t arg_pos)
615 {
616 	switch (Z_TYPE_P(val)) {
617 	case IS_LONG:
618 		mpz_set_si(gmpnumber, Z_LVAL_P(val));
619 		return SUCCESS;
620 	case IS_STRING: {
621 		return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos);
622 	}
623 	default: {
624 		zend_long lval;
625 		if (!zend_parse_arg_long_slow(val, &lval, arg_pos)) {
626 			if (arg_pos == 0) {
627 				zend_type_error(
628 					"Number must be of type GMP|string|int, %s given", zend_zval_value_name(val));
629 			} else {
630 				zend_argument_type_error(arg_pos,
631 					"must be of type GMP|string|int, %s given", zend_zval_value_name(val));
632 			}
633 			return FAILURE;
634 		}
635 
636 		mpz_set_si(gmpnumber, lval);
637 		return SUCCESS;
638 	}
639 	}
640 }
641 /* }}} */
642 
gmp_strval(zval * result,mpz_t gmpnum,int base)643 static void gmp_strval(zval *result, mpz_t gmpnum, int base) /* {{{ */
644 {
645 	size_t num_len;
646 	zend_string *str;
647 
648 	num_len = mpz_sizeinbase(gmpnum, abs(base));
649 	if (mpz_sgn(gmpnum) < 0) {
650 		num_len++;
651 	}
652 
653 	str = zend_string_alloc(num_len, 0);
654 	mpz_get_str(ZSTR_VAL(str), base, gmpnum);
655 
656 	/*
657 	 * From GMP documentation for mpz_sizeinbase():
658 	 * The returned value will be exact or 1 too big.  If base is a power of
659 	 * 2, the returned value will always be exact.
660 	 *
661 	 * So let's check to see if we already have a \0 byte...
662 	 */
663 
664 	if (ZSTR_VAL(str)[ZSTR_LEN(str) - 1] == '\0') {
665 		ZSTR_LEN(str)--;
666 	} else {
667 		ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
668 	}
669 
670 	ZVAL_NEW_STR(result, str);
671 }
672 /* }}} */
673 
gmp_cmp(zval * return_value,zval * a_arg,zval * b_arg,bool is_operator)674 static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operator) /* {{{ */
675 {
676 	mpz_ptr gmpnum_a, gmpnum_b;
677 	gmp_temp_t temp_a, temp_b;
678 	bool use_si = 0;
679 	zend_long res;
680 
681 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
682 
683 	if (Z_TYPE_P(b_arg) == IS_LONG) {
684 		use_si = 1;
685 		temp_b.is_used = 0;
686 	} else {
687 		FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
688 	}
689 
690 	if (use_si) {
691 		res = mpz_cmp_si(gmpnum_a, Z_LVAL_P(b_arg));
692 	} else {
693 		res = mpz_cmp(gmpnum_a, gmpnum_b);
694 	}
695 
696 	FREE_GMP_TEMP(temp_a);
697 	FREE_GMP_TEMP(temp_b);
698 
699 	RETURN_LONG(res);
700 }
701 /* }}} */
702 
703 /* {{{ gmp_zval_binary_ui_op
704    Execute GMP binary operation.
705 */
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)706 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)
707 {
708 	mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
709 	gmp_temp_t temp_a, temp_b;
710 
711 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
712 
713 	if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
714 		gmpnum_b = NULL;
715 		temp_b.is_used = 0;
716 	} else {
717 		FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
718 	}
719 
720 	if (check_b_zero) {
721 		int b_is_zero = 0;
722 		if (!gmpnum_b) {
723 			b_is_zero = (Z_LVAL_P(b_arg) == 0);
724 		} else {
725 			b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
726 		}
727 
728 		if (b_is_zero) {
729 			if ((gmp_binary_op_t) mpz_mod == gmp_op) {
730 				zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
731 			} else {
732 				zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
733 			}
734 			FREE_GMP_TEMP(temp_a);
735 			FREE_GMP_TEMP(temp_b);
736 			RETURN_THROWS();
737 		}
738 	}
739 
740 	INIT_GMP_RETVAL(gmpnum_result);
741 
742 	if (!gmpnum_b) {
743 		gmp_ui_op(gmpnum_result, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
744 	} else {
745 		gmp_op(gmpnum_result, gmpnum_a, gmpnum_b);
746 	}
747 
748 	FREE_GMP_TEMP(temp_a);
749 	FREE_GMP_TEMP(temp_b);
750 }
751 /* }}} */
752 
753 /* {{{ gmp_zval_binary_ui_op2
754    Execute GMP binary operation which returns 2 values.
755 */
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)756 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)
757 {
758 	mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result1, gmpnum_result2;
759 	gmp_temp_t temp_a, temp_b;
760 	zval result1, result2;
761 
762 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
763 
764 	if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
765 		gmpnum_b = NULL;
766 		temp_b.is_used = 0;
767 	} else {
768 		FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
769 	}
770 
771 	if (check_b_zero) {
772 		int b_is_zero = 0;
773 		if (!gmpnum_b) {
774 			b_is_zero = (Z_LVAL_P(b_arg) == 0);
775 		} else {
776 			b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
777 		}
778 
779 		if (b_is_zero) {
780 			zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
781 			FREE_GMP_TEMP(temp_a);
782 			FREE_GMP_TEMP(temp_b);
783 			RETURN_THROWS();
784 		}
785 	}
786 
787 	gmp_create(&result1, &gmpnum_result1);
788 	gmp_create(&result2, &gmpnum_result2);
789 
790 	array_init(return_value);
791 	add_next_index_zval(return_value, &result1);
792 	add_next_index_zval(return_value, &result2);
793 
794 	if (!gmpnum_b) {
795 		gmp_ui_op(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
796 	} else {
797 		gmp_op(gmpnum_result1, gmpnum_result2, gmpnum_a, gmpnum_b);
798 	}
799 
800 	FREE_GMP_TEMP(temp_a);
801 	FREE_GMP_TEMP(temp_b);
802 }
803 /* }}} */
804 
805 /* {{{ _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)806 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)
807 {
808 	zval *a_arg, *b_arg;
809 
810 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
811 		RETURN_THROWS();
812 	}
813 
814 	gmp_zval_binary_ui_op(
815 		return_value, a_arg, b_arg, gmp_op, gmp_ui_op, check_b_zero, /* is_operator */ false);
816 }
817 /* }}} */
818 
819 /* Unary operations */
820 
821 /* {{{ gmp_zval_unary_op */
gmp_zval_unary_op(zval * return_value,zval * a_arg,gmp_unary_op_t gmp_op)822 static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op)
823 {
824 	mpz_ptr gmpnum_a, gmpnum_result;
825 	gmp_temp_t temp_a;
826 
827 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
828 
829 	INIT_GMP_RETVAL(gmpnum_result);
830 	gmp_op(gmpnum_result, gmpnum_a);
831 
832 	FREE_GMP_TEMP(temp_a);
833 }
834 /* }}} */
835 
836 /* {{{ _gmp_unary_op */
_gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS,gmp_unary_op_t gmp_op)837 static inline void _gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_op_t gmp_op)
838 {
839 	zval *a_arg;
840 
841 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
842 		RETURN_THROWS();
843 	}
844 
845 	gmp_zval_unary_op(return_value, a_arg, gmp_op);
846 }
847 /* }}} */
848 
849 /* {{{ _gmp_unary_opl */
_gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS,gmp_unary_opl_t gmp_op)850 static inline void _gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_opl_t gmp_op)
851 {
852 	zval *a_arg;
853 	mpz_ptr gmpnum_a;
854 	gmp_temp_t temp_a;
855 
856 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
857 		RETURN_THROWS();
858 	}
859 
860 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
861 	RETVAL_LONG(gmp_op(gmpnum_a));
862 	FREE_GMP_TEMP(temp_a);
863 }
864 /* }}} */
865 
gmp_verify_base(zend_long base,uint32_t arg_num)866 static bool gmp_verify_base(zend_long base, uint32_t arg_num)
867 {
868 	if (base && (base < 2 || base > GMP_MAX_BASE)) {
869 		zend_argument_value_error(arg_num, "must be between 2 and %d", GMP_MAX_BASE);
870 		return false;
871 	}
872 
873 	return true;
874 }
875 
gmp_initialize_number(mpz_ptr gmp_number,const zend_string * arg_str,zend_long arg_l,zend_long base)876 static zend_result gmp_initialize_number(mpz_ptr gmp_number, const zend_string *arg_str, zend_long arg_l, zend_long base)
877 {
878 	if (arg_str) {
879 		return convert_zstr_to_gmp(gmp_number, arg_str, base, 1);
880 	}
881 
882 	mpz_set_si(gmp_number, arg_l);
883 	return SUCCESS;
884 }
885 
886 /* {{{ Initializes GMP number */
ZEND_FUNCTION(gmp_init)887 ZEND_FUNCTION(gmp_init)
888 {
889 	mpz_ptr gmp_number;
890 	zend_string *arg_str = NULL;
891 	zend_long arg_l = 0;
892 	zend_long base = 0;
893 
894 	ZEND_PARSE_PARAMETERS_START(1, 2)
895 		Z_PARAM_STR_OR_LONG(arg_str, arg_l)
896 		Z_PARAM_OPTIONAL
897 		Z_PARAM_LONG(base)
898 	ZEND_PARSE_PARAMETERS_END();
899 
900 	if (!gmp_verify_base(base, 2)) {
901 		RETURN_THROWS();
902 	}
903 
904 	INIT_GMP_RETVAL(gmp_number);
905 
906 	if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
907 		RETURN_THROWS();
908 	}
909 }
910 /* }}} */
911 
gmp_import_export_validate(zend_long size,zend_long options,int * order,int * endian)912 int gmp_import_export_validate(zend_long size, zend_long options, int *order, int *endian)
913 {
914 	if (size < 1) {
915 		/* size argument is in second position */
916 		zend_argument_value_error(2, "must be greater than or equal to 1");
917 		return FAILURE;
918 	}
919 
920 	switch (options & (GMP_LSW_FIRST | GMP_MSW_FIRST)) {
921 		case GMP_LSW_FIRST:
922 			*order = -1;
923 			break;
924 		case GMP_MSW_FIRST:
925 		case 0: /* default */
926 			*order = 1;
927 			break;
928 		default:
929 			/* options argument is in second position */
930 			zend_argument_value_error(3, "cannot use multiple word order options");
931 			return FAILURE;
932 	}
933 
934 	switch (options & (GMP_LITTLE_ENDIAN | GMP_BIG_ENDIAN | GMP_NATIVE_ENDIAN)) {
935 		case GMP_LITTLE_ENDIAN:
936 			*endian = -1;
937 			break;
938 		case GMP_BIG_ENDIAN:
939 			*endian = 1;
940 			break;
941 		case GMP_NATIVE_ENDIAN:
942 		case 0: /* default */
943 			*endian = 0;
944 			break;
945 		default:
946 			/* options argument is in second position */
947 			zend_argument_value_error(3, "cannot use multiple endian options");
948 			return FAILURE;
949 	}
950 
951 	return SUCCESS;
952 }
953 
954 /* {{{ Imports a GMP number from a binary string */
ZEND_FUNCTION(gmp_import)955 ZEND_FUNCTION(gmp_import)
956 {
957 	char *data;
958 	size_t data_len;
959 	zend_long size = 1;
960 	zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
961 	int order, endian;
962 	mpz_ptr gmpnumber;
963 
964 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &data, &data_len, &size, &options) == FAILURE) {
965 		RETURN_THROWS();
966 	}
967 
968 	if (gmp_import_export_validate(size, options, &order, &endian) == FAILURE) {
969 		RETURN_THROWS();
970 	}
971 
972 	if ((data_len % size) != 0) {
973 		zend_argument_value_error(1, "must be a multiple of argument #2 ($word_size)");
974 		RETURN_THROWS();
975 	}
976 
977 	INIT_GMP_RETVAL(gmpnumber);
978 
979 	mpz_import(gmpnumber, data_len / size, order, size, endian, 0, data);
980 }
981 /* }}} */
982 
983 /* {{{ Exports a GMP number to a binary string */
ZEND_FUNCTION(gmp_export)984 ZEND_FUNCTION(gmp_export)
985 {
986 	zval *gmpnumber_arg;
987 	zend_long size = 1;
988 	zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
989 	int order, endian;
990 	mpz_ptr gmpnumber;
991 	gmp_temp_t temp_a;
992 
993 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|ll", &gmpnumber_arg, &size, &options) == FAILURE) {
994 		RETURN_THROWS();
995 	}
996 
997 	if (gmp_import_export_validate(size, options, &order, &endian) == FAILURE) {
998 		RETURN_THROWS();
999 	}
1000 
1001 	FETCH_GMP_ZVAL(gmpnumber, gmpnumber_arg, temp_a, 1);
1002 
1003 	if (mpz_sgn(gmpnumber) == 0) {
1004 		RETVAL_EMPTY_STRING();
1005 	} else {
1006 		size_t bits_per_word = size * 8;
1007 		size_t count = (mpz_sizeinbase(gmpnumber, 2) + bits_per_word - 1) / bits_per_word;
1008 
1009 		zend_string *out_string = zend_string_safe_alloc(count, size, 0, 0);
1010 		mpz_export(ZSTR_VAL(out_string), NULL, order, size, endian, 0, gmpnumber);
1011 		ZSTR_VAL(out_string)[ZSTR_LEN(out_string)] = '\0';
1012 
1013 		RETVAL_NEW_STR(out_string);
1014 	}
1015 
1016 	FREE_GMP_TEMP(temp_a);
1017 }
1018 /* }}} */
1019 
1020 /* {{{ Gets signed long value of GMP number */
ZEND_FUNCTION(gmp_intval)1021 ZEND_FUNCTION(gmp_intval)
1022 {
1023 	zval *gmpnumber_arg;
1024 	mpz_ptr gmpnum;
1025 	gmp_temp_t temp_a;
1026 
1027 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &gmpnumber_arg) == FAILURE){
1028 		RETURN_THROWS();
1029 	}
1030 
1031 	FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a, 1);
1032 	RETVAL_LONG(mpz_get_si(gmpnum));
1033 	FREE_GMP_TEMP(temp_a);
1034 }
1035 /* }}} */
1036 
1037 /* {{{ Gets string representation of GMP number  */
ZEND_FUNCTION(gmp_strval)1038 ZEND_FUNCTION(gmp_strval)
1039 {
1040 	zval *gmpnumber_arg;
1041 	zend_long base = 10;
1042 	mpz_ptr gmpnum;
1043 	gmp_temp_t temp_a;
1044 
1045 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &base) == FAILURE) {
1046 		RETURN_THROWS();
1047 	}
1048 
1049 	/* Although the maximum base in general in GMP is 62, mpz_get_str()
1050 	 * is explicitly limited to -36 when dealing with negative bases. */
1051 	if ((base < 2 && base > -2) || base > GMP_MAX_BASE || base < -36) {
1052 		zend_argument_value_error(2, "must be between 2 and %d, or -2 and -36", GMP_MAX_BASE);
1053 		RETURN_THROWS();
1054 	}
1055 
1056 	FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a, 1);
1057 
1058 	gmp_strval(return_value, gmpnum, (int)base);
1059 
1060 	FREE_GMP_TEMP(temp_a);
1061 }
1062 /* }}} */
1063 
1064 /* {{{ Add a and b */
ZEND_FUNCTION(gmp_add)1065 ZEND_FUNCTION(gmp_add)
1066 {
1067 	gmp_binary_ui_op(mpz_add, mpz_add_ui);
1068 }
1069 /* }}} */
1070 
1071 /* {{{ Subtract b from a */
ZEND_FUNCTION(gmp_sub)1072 ZEND_FUNCTION(gmp_sub)
1073 {
1074 	gmp_binary_ui_op(mpz_sub, mpz_sub_ui);
1075 }
1076 /* }}} */
1077 
1078 /* {{{ Multiply a and b */
ZEND_FUNCTION(gmp_mul)1079 ZEND_FUNCTION(gmp_mul)
1080 {
1081 	gmp_binary_ui_op(mpz_mul, mpz_mul_ui);
1082 }
1083 /* }}} */
1084 
1085 /* {{{ Divide a by b, returns quotient and reminder */
ZEND_FUNCTION(gmp_div_qr)1086 ZEND_FUNCTION(gmp_div_qr)
1087 {
1088 	zval *a_arg, *b_arg;
1089 	zend_long round = GMP_ROUND_ZERO;
1090 
1091 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1092 		RETURN_THROWS();
1093 	}
1094 
1095 	switch (round) {
1096 	case GMP_ROUND_ZERO:
1097 		gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_tdiv_qr, mpz_tdiv_qr_ui, 1);
1098 		break;
1099 	case GMP_ROUND_PLUSINF:
1100 		gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_cdiv_qr, mpz_cdiv_qr_ui, 1);
1101 		break;
1102 	case GMP_ROUND_MINUSINF:
1103 		gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_fdiv_qr, mpz_fdiv_qr_ui, 1);
1104 		break;
1105 	default:
1106 		zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1107 		RETURN_THROWS();
1108 	}
1109 }
1110 /* }}} */
1111 
1112 /* {{{ Divide a by b, returns reminder only */
ZEND_FUNCTION(gmp_div_r)1113 ZEND_FUNCTION(gmp_div_r)
1114 {
1115 	zval *a_arg, *b_arg;
1116 	zend_long round = GMP_ROUND_ZERO;
1117 
1118 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1119 		RETURN_THROWS();
1120 	}
1121 
1122 	switch (round) {
1123 	case GMP_ROUND_ZERO:
1124 		gmp_zval_binary_ui_op(
1125 			return_value, a_arg, b_arg, mpz_tdiv_r, gmp_mpz_tdiv_r_ui, 1, /* is_operator */ false);
1126 		break;
1127 	case GMP_ROUND_PLUSINF:
1128 		gmp_zval_binary_ui_op(
1129 			return_value, a_arg, b_arg, mpz_cdiv_r, gmp_mpz_cdiv_r_ui, 1, /* is_operator */ false);
1130 		break;
1131 	case GMP_ROUND_MINUSINF:
1132 		gmp_zval_binary_ui_op(
1133 			return_value, a_arg, b_arg, mpz_fdiv_r, gmp_mpz_fdiv_r_ui, 1, /* is_operator */ false);
1134 		break;
1135 	default:
1136 		zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1137 		RETURN_THROWS();
1138 	}
1139 }
1140 /* }}} */
1141 
1142 /* {{{ Divide a by b, returns quotient only */
ZEND_FUNCTION(gmp_div_q)1143 ZEND_FUNCTION(gmp_div_q)
1144 {
1145 	zval *a_arg, *b_arg;
1146 	zend_long round = GMP_ROUND_ZERO;
1147 
1148 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1149 		RETURN_THROWS();
1150 	}
1151 
1152 	switch (round) {
1153 	case GMP_ROUND_ZERO:
1154 		gmp_zval_binary_ui_op(
1155 			return_value, a_arg, b_arg, mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1, /* is_operator */ false);
1156 		break;
1157 	case GMP_ROUND_PLUSINF:
1158 		gmp_zval_binary_ui_op(
1159 			return_value, a_arg, b_arg, mpz_cdiv_q, gmp_mpz_cdiv_q_ui, 1, /* is_operator */ false);
1160 		break;
1161 	case GMP_ROUND_MINUSINF:
1162 		gmp_zval_binary_ui_op(
1163 			return_value, a_arg, b_arg, mpz_fdiv_q, gmp_mpz_fdiv_q_ui, 1, /* is_operator */ false);
1164 		break;
1165 	default:
1166 		zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1167 		RETURN_THROWS();
1168 	}
1169 
1170 }
1171 /* }}} */
1172 
1173 /* {{{ Computes a modulo b */
ZEND_FUNCTION(gmp_mod)1174 ZEND_FUNCTION(gmp_mod)
1175 {
1176 	gmp_binary_ui_op_no_zero(mpz_mod, gmp_mpz_mod_ui);
1177 }
1178 /* }}} */
1179 
1180 /* {{{ Divide a by b using exact division algorithm */
ZEND_FUNCTION(gmp_divexact)1181 ZEND_FUNCTION(gmp_divexact)
1182 {
1183 	gmp_binary_ui_op_no_zero(mpz_divexact, NULL);
1184 }
1185 /* }}} */
1186 
1187 /* {{{ Negates a number */
ZEND_FUNCTION(gmp_neg)1188 ZEND_FUNCTION(gmp_neg)
1189 {
1190 	gmp_unary_op(mpz_neg);
1191 }
1192 /* }}} */
1193 
1194 /* {{{ Calculates absolute value */
ZEND_FUNCTION(gmp_abs)1195 ZEND_FUNCTION(gmp_abs)
1196 {
1197 	gmp_unary_op(mpz_abs);
1198 }
1199 /* }}} */
1200 
1201 /* {{{ Calculates factorial function */
ZEND_FUNCTION(gmp_fact)1202 ZEND_FUNCTION(gmp_fact)
1203 {
1204 	zval *a_arg;
1205 	mpz_ptr gmpnum_result;
1206 
1207 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1208 		RETURN_THROWS();
1209 	}
1210 
1211 	if (Z_TYPE_P(a_arg) == IS_LONG) {
1212 		if (Z_LVAL_P(a_arg) < 0) {
1213 			zend_argument_value_error(1, "must be greater than or equal to 0");
1214 			RETURN_THROWS();
1215 		}
1216 	} else {
1217 		mpz_ptr gmpnum;
1218 		gmp_temp_t temp_a;
1219 
1220 		FETCH_GMP_ZVAL(gmpnum, a_arg, temp_a, 1);
1221 		FREE_GMP_TEMP(temp_a);
1222 
1223 		if (mpz_sgn(gmpnum) < 0) {
1224 			zend_argument_value_error(1, "must be greater than or equal to 0");
1225 			RETURN_THROWS();
1226 		}
1227 	}
1228 
1229 	INIT_GMP_RETVAL(gmpnum_result);
1230 	mpz_fac_ui(gmpnum_result, zval_get_long(a_arg));
1231 }
1232 /* }}} */
1233 
1234 /* {{{ Calculates binomial coefficient */
ZEND_FUNCTION(gmp_binomial)1235 ZEND_FUNCTION(gmp_binomial)
1236 {
1237 	zval *n_arg;
1238 	zend_long k;
1239 	mpz_ptr gmpnum_result;
1240 
1241 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &n_arg, &k) == FAILURE) {
1242 		RETURN_THROWS();
1243 	}
1244 
1245 	if (k < 0) {
1246 		zend_argument_value_error(2, "must be greater than or equal to 0");
1247 		RETURN_THROWS();
1248 	}
1249 
1250 	INIT_GMP_RETVAL(gmpnum_result);
1251 	if (Z_TYPE_P(n_arg) == IS_LONG && Z_LVAL_P(n_arg) >= 0) {
1252 		mpz_bin_uiui(gmpnum_result, (gmp_ulong) Z_LVAL_P(n_arg), (gmp_ulong) k);
1253 	} else {
1254 		mpz_ptr gmpnum_n;
1255 		gmp_temp_t temp_n;
1256 		FETCH_GMP_ZVAL(gmpnum_n, n_arg, temp_n, 1);
1257 		mpz_bin_ui(gmpnum_result, gmpnum_n, (gmp_ulong) k);
1258 		FREE_GMP_TEMP(temp_n);
1259 	}
1260 }
1261 /* }}} */
1262 
1263 /* {{{ Raise base to power exp */
ZEND_FUNCTION(gmp_pow)1264 ZEND_FUNCTION(gmp_pow)
1265 {
1266 	zval *base_arg;
1267 	mpz_ptr gmpnum_result;
1268 	gmp_temp_t temp_base;
1269 	zend_long exp;
1270 
1271 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &base_arg, &exp) == FAILURE) {
1272 		RETURN_THROWS();
1273 	}
1274 
1275 	if (exp < 0) {
1276 		zend_argument_value_error(2, "must be greater than or equal to 0");
1277 		RETURN_THROWS();
1278 	}
1279 
1280 	if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) {
1281 		INIT_GMP_RETVAL(gmpnum_result);
1282 		mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
1283 	} else {
1284 		mpz_ptr gmpnum_base;
1285 		FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
1286 		INIT_GMP_RETVAL(gmpnum_result);
1287 		mpz_pow_ui(gmpnum_result, gmpnum_base, exp);
1288 		FREE_GMP_TEMP(temp_base);
1289 	}
1290 }
1291 /* }}} */
1292 
1293 /* {{{ Raise base to power exp and take result modulo mod */
ZEND_FUNCTION(gmp_powm)1294 ZEND_FUNCTION(gmp_powm)
1295 {
1296 	zval *base_arg, *exp_arg, *mod_arg;
1297 	mpz_ptr gmpnum_base, gmpnum_exp, gmpnum_mod, gmpnum_result;
1298 	int use_ui = 0;
1299 	gmp_temp_t temp_base, temp_exp, temp_mod;
1300 
1301 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz", &base_arg, &exp_arg, &mod_arg) == FAILURE){
1302 		RETURN_THROWS();
1303 	}
1304 
1305 	FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
1306 
1307 	if (Z_TYPE_P(exp_arg) == IS_LONG && Z_LVAL_P(exp_arg) >= 0) {
1308 		use_ui = 1;
1309 		temp_exp.is_used = 0;
1310 	} else {
1311 		FETCH_GMP_ZVAL_DEP(gmpnum_exp, exp_arg, temp_exp, temp_base, 2);
1312 		if (mpz_sgn(gmpnum_exp) < 0) {
1313 			zend_argument_value_error(2, "must be greater than or equal to 0");
1314 			FREE_GMP_TEMP(temp_base);
1315 			FREE_GMP_TEMP(temp_exp);
1316 			RETURN_THROWS();
1317 		}
1318 	}
1319 	FETCH_GMP_ZVAL_DEP_DEP(gmpnum_mod, mod_arg, temp_mod, temp_exp, temp_base, 3);
1320 
1321 	if (!mpz_cmp_ui(gmpnum_mod, 0)) {
1322 		zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
1323 		FREE_GMP_TEMP(temp_base);
1324 		FREE_GMP_TEMP(temp_exp);
1325 		FREE_GMP_TEMP(temp_mod);
1326 		RETURN_THROWS();
1327 	}
1328 
1329 	INIT_GMP_RETVAL(gmpnum_result);
1330 	if (use_ui) {
1331 		mpz_powm_ui(gmpnum_result, gmpnum_base, (zend_ulong) Z_LVAL_P(exp_arg), gmpnum_mod);
1332 	} else {
1333 		mpz_powm(gmpnum_result, gmpnum_base, gmpnum_exp, gmpnum_mod);
1334 		FREE_GMP_TEMP(temp_exp);
1335 	}
1336 
1337 	FREE_GMP_TEMP(temp_base);
1338 	FREE_GMP_TEMP(temp_mod);
1339 }
1340 /* }}} */
1341 
1342 /* {{{ Takes integer part of square root of a */
ZEND_FUNCTION(gmp_sqrt)1343 ZEND_FUNCTION(gmp_sqrt)
1344 {
1345 	zval *a_arg;
1346 	mpz_ptr gmpnum_a, gmpnum_result;
1347 	gmp_temp_t temp_a;
1348 
1349 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1350 		RETURN_THROWS();
1351 	}
1352 
1353 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1354 
1355 	if (mpz_sgn(gmpnum_a) < 0) {
1356 		zend_argument_value_error(1, "must be greater than or equal to 0");
1357 		FREE_GMP_TEMP(temp_a);
1358 		RETURN_THROWS();
1359 	}
1360 
1361 	INIT_GMP_RETVAL(gmpnum_result);
1362 	mpz_sqrt(gmpnum_result, gmpnum_a);
1363 	FREE_GMP_TEMP(temp_a);
1364 }
1365 /* }}} */
1366 
1367 /* {{{ Square root with remainder */
ZEND_FUNCTION(gmp_sqrtrem)1368 ZEND_FUNCTION(gmp_sqrtrem)
1369 {
1370 	zval *a_arg;
1371 	mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
1372 	gmp_temp_t temp_a;
1373 	zval result1, result2;
1374 
1375 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1376 		RETURN_THROWS();
1377 	}
1378 
1379 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1380 
1381 	if (mpz_sgn(gmpnum_a) < 0) {
1382 		zend_argument_value_error(1, "must be greater than or equal to 0");
1383 		FREE_GMP_TEMP(temp_a);
1384 		RETURN_THROWS();
1385 	}
1386 
1387 	gmp_create(&result1, &gmpnum_result1);
1388 	gmp_create(&result2, &gmpnum_result2);
1389 
1390 	array_init(return_value);
1391 	add_next_index_zval(return_value, &result1);
1392 	add_next_index_zval(return_value, &result2);
1393 
1394 	mpz_sqrtrem(gmpnum_result1, gmpnum_result2, gmpnum_a);
1395 	FREE_GMP_TEMP(temp_a);
1396 }
1397 /* }}} */
1398 
1399 /* {{{ Takes integer part of nth root */
ZEND_FUNCTION(gmp_root)1400 ZEND_FUNCTION(gmp_root)
1401 {
1402 	zval *a_arg;
1403 	zend_long nth;
1404 	mpz_ptr gmpnum_a, gmpnum_result;
1405 	gmp_temp_t temp_a;
1406 
1407 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
1408 		RETURN_THROWS();
1409 	}
1410 
1411 	if (nth <= 0) {
1412 		zend_argument_value_error(2, "must be greater than 0");
1413 		RETURN_THROWS();
1414 	}
1415 
1416 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1417 
1418 	if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
1419 		zend_argument_value_error(2, "must be odd if argument #1 ($a) is negative");
1420 		FREE_GMP_TEMP(temp_a);
1421 		RETURN_THROWS();
1422 	}
1423 
1424 	INIT_GMP_RETVAL(gmpnum_result);
1425 	mpz_root(gmpnum_result, gmpnum_a, (gmp_ulong) nth);
1426 	FREE_GMP_TEMP(temp_a);
1427 }
1428 /* }}} */
1429 
1430 /* {{{ Calculates integer part of nth root and remainder */
ZEND_FUNCTION(gmp_rootrem)1431 ZEND_FUNCTION(gmp_rootrem)
1432 {
1433 	zval *a_arg;
1434 	zend_long nth;
1435 	mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
1436 	gmp_temp_t temp_a;
1437 	zval result1, result2;
1438 
1439 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
1440 		RETURN_THROWS();
1441 	}
1442 
1443 	if (nth <= 0) {
1444 		zend_argument_value_error(2, "must be greater than or equal to 1");
1445 		RETURN_THROWS();
1446 	}
1447 
1448 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1449 
1450 	if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
1451 		zend_argument_value_error(2, "must be odd if argument #1 ($a) is negative");
1452 		FREE_GMP_TEMP(temp_a);
1453 		RETURN_THROWS();
1454 	}
1455 
1456 	gmp_create(&result1, &gmpnum_result1);
1457 	gmp_create(&result2, &gmpnum_result2);
1458 
1459 	array_init(return_value);
1460 	add_next_index_zval(return_value, &result1);
1461 	add_next_index_zval(return_value, &result2);
1462 
1463 #if GMP_51_OR_NEWER
1464 	/* mpz_rootrem() is supported since GMP 4.2, but buggy wrt odd roots
1465 	 * of negative numbers */
1466 	mpz_rootrem(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) nth);
1467 #else
1468 	mpz_root(gmpnum_result1, gmpnum_a, (gmp_ulong) nth);
1469 	mpz_pow_ui(gmpnum_result2, gmpnum_result1, (gmp_ulong) nth);
1470 	mpz_sub(gmpnum_result2, gmpnum_a, gmpnum_result2);
1471 #endif
1472 
1473 	FREE_GMP_TEMP(temp_a);
1474 }
1475 /* }}} */
1476 
1477 /* {{{ Checks if a is an exact square */
ZEND_FUNCTION(gmp_perfect_square)1478 ZEND_FUNCTION(gmp_perfect_square)
1479 {
1480 	zval *a_arg;
1481 	mpz_ptr gmpnum_a;
1482 	gmp_temp_t temp_a;
1483 
1484 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1485 		RETURN_THROWS();
1486 	}
1487 
1488 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1489 
1490 	RETVAL_BOOL((mpz_perfect_square_p(gmpnum_a) != 0));
1491 	FREE_GMP_TEMP(temp_a);
1492 }
1493 /* }}} */
1494 
1495 /* {{{ Checks if a is a perfect power */
ZEND_FUNCTION(gmp_perfect_power)1496 ZEND_FUNCTION(gmp_perfect_power)
1497 {
1498 	zval *a_arg;
1499 	mpz_ptr gmpnum_a;
1500 	gmp_temp_t temp_a;
1501 
1502 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1503 		RETURN_THROWS();
1504 	}
1505 
1506 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1507 
1508 	RETVAL_BOOL((mpz_perfect_power_p(gmpnum_a) != 0));
1509 	FREE_GMP_TEMP(temp_a);
1510 }
1511 /* }}} */
1512 
1513 /* {{{ Checks if a is "probably prime" */
ZEND_FUNCTION(gmp_prob_prime)1514 ZEND_FUNCTION(gmp_prob_prime)
1515 {
1516 	zval *gmpnumber_arg;
1517 	mpz_ptr gmpnum_a;
1518 	zend_long reps = 10;
1519 	gmp_temp_t temp_a;
1520 
1521 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &reps) == FAILURE) {
1522 		RETURN_THROWS();
1523 	}
1524 
1525 	FETCH_GMP_ZVAL(gmpnum_a, gmpnumber_arg, temp_a, 1);
1526 
1527 	RETVAL_LONG(mpz_probab_prime_p(gmpnum_a, (int)reps));
1528 	FREE_GMP_TEMP(temp_a);
1529 }
1530 /* }}} */
1531 
1532 /* {{{ Computes greatest common denominator (gcd) of a and b */
ZEND_FUNCTION(gmp_gcd)1533 ZEND_FUNCTION(gmp_gcd)
1534 {
1535 	gmp_binary_ui_op(mpz_gcd, gmp_mpz_gcd_ui);
1536 }
1537 /* }}} */
1538 
1539 /* {{{ Computes least common multiple (lcm) of a and b */
ZEND_FUNCTION(gmp_lcm)1540 ZEND_FUNCTION(gmp_lcm)
1541 {
1542 	gmp_binary_ui_op(mpz_lcm, mpz_lcm_ui);
1543 }
1544 /* }}} */
1545 
1546 /* {{{ Computes G, S, and T, such that AS + BT = G = `gcd' (A, B) */
ZEND_FUNCTION(gmp_gcdext)1547 ZEND_FUNCTION(gmp_gcdext)
1548 {
1549 	zval *a_arg, *b_arg;
1550 	mpz_ptr gmpnum_a, gmpnum_b, gmpnum_t, gmpnum_s, gmpnum_g;
1551 	gmp_temp_t temp_a, temp_b;
1552 	zval result_g, result_s, result_t;
1553 
1554 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1555 		RETURN_THROWS();
1556 	}
1557 
1558 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1559 	FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1560 
1561 	gmp_create(&result_g, &gmpnum_g);
1562 	gmp_create(&result_s, &gmpnum_s);
1563 	gmp_create(&result_t, &gmpnum_t);
1564 
1565 	array_init(return_value);
1566 	add_assoc_zval(return_value, "g", &result_g);
1567 	add_assoc_zval(return_value, "s", &result_s);
1568 	add_assoc_zval(return_value, "t", &result_t);
1569 
1570 	mpz_gcdext(gmpnum_g, gmpnum_s, gmpnum_t, gmpnum_a, gmpnum_b);
1571 	FREE_GMP_TEMP(temp_a);
1572 	FREE_GMP_TEMP(temp_b);
1573 }
1574 /* }}} */
1575 
1576 /* {{{ Computes the inverse of a modulo b */
ZEND_FUNCTION(gmp_invert)1577 ZEND_FUNCTION(gmp_invert)
1578 {
1579 	zval *a_arg, *b_arg;
1580 	mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
1581 	gmp_temp_t temp_a, temp_b;
1582 	int res;
1583 
1584 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1585 		RETURN_THROWS();
1586 	}
1587 
1588 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1589 	FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1590 
1591 	// TODO Early check if b_arg IS_LONG?
1592 	if (0 == mpz_cmp_ui(gmpnum_b, 0)) {
1593 		zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1594 		FREE_GMP_TEMP(temp_a);
1595 		FREE_GMP_TEMP(temp_b);
1596 		RETURN_THROWS();
1597 	}
1598 
1599 	INIT_GMP_RETVAL(gmpnum_result);
1600 	res = mpz_invert(gmpnum_result, gmpnum_a, gmpnum_b);
1601 	FREE_GMP_TEMP(temp_a);
1602 	FREE_GMP_TEMP(temp_b);
1603 	if (!res) {
1604 		zval_ptr_dtor(return_value);
1605 		RETURN_FALSE;
1606 	}
1607 }
1608 /* }}} */
1609 
1610 /* {{{ Computes Jacobi symbol */
ZEND_FUNCTION(gmp_jacobi)1611 ZEND_FUNCTION(gmp_jacobi)
1612 {
1613 	zval *a_arg, *b_arg;
1614 	mpz_ptr gmpnum_a, gmpnum_b;
1615 	gmp_temp_t temp_a, temp_b;
1616 
1617 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1618 		RETURN_THROWS();
1619 	}
1620 
1621 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1622 	FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1623 
1624 	RETVAL_LONG(mpz_jacobi(gmpnum_a, gmpnum_b));
1625 
1626 	FREE_GMP_TEMP(temp_a);
1627 	FREE_GMP_TEMP(temp_b);
1628 }
1629 /* }}} */
1630 
1631 /* {{{ Computes Legendre symbol */
ZEND_FUNCTION(gmp_legendre)1632 ZEND_FUNCTION(gmp_legendre)
1633 {
1634 	zval *a_arg, *b_arg;
1635 	mpz_ptr gmpnum_a, gmpnum_b;
1636 	gmp_temp_t temp_a, temp_b;
1637 
1638 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1639 		RETURN_THROWS();
1640 	}
1641 
1642 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1643 	FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1644 
1645 	RETVAL_LONG(mpz_legendre(gmpnum_a, gmpnum_b));
1646 
1647 	FREE_GMP_TEMP(temp_a);
1648 	FREE_GMP_TEMP(temp_b);
1649 }
1650 /* }}} */
1651 
1652 /* {{{ Computes the Kronecker symbol */
ZEND_FUNCTION(gmp_kronecker)1653 ZEND_FUNCTION(gmp_kronecker)
1654 {
1655 	zval *a_arg, *b_arg;
1656 	mpz_ptr gmpnum_a, gmpnum_b;
1657 	gmp_temp_t temp_a, temp_b;
1658 	bool use_a_si = 0, use_b_si = 0;
1659 	int result;
1660 
1661 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1662 		RETURN_THROWS();
1663 	}
1664 
1665 	if (Z_TYPE_P(a_arg) == IS_LONG && Z_TYPE_P(b_arg) != IS_LONG) {
1666 		use_a_si = 1;
1667 		temp_a.is_used = 0;
1668 	} else {
1669 		FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1670 	}
1671 
1672 	if (Z_TYPE_P(b_arg) == IS_LONG) {
1673 		use_b_si = 1;
1674 		temp_b.is_used = 0;
1675 	} else {
1676 		FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1677 	}
1678 
1679 	if (use_a_si) {
1680 		ZEND_ASSERT(use_b_si == 0);
1681 		result = mpz_si_kronecker((gmp_long) Z_LVAL_P(a_arg), gmpnum_b);
1682 	} else if (use_b_si) {
1683 		result = mpz_kronecker_si(gmpnum_a, (gmp_long) Z_LVAL_P(b_arg));
1684 	} else {
1685 		result = mpz_kronecker(gmpnum_a, gmpnum_b);
1686 	}
1687 
1688 	FREE_GMP_TEMP(temp_a);
1689 	FREE_GMP_TEMP(temp_b);
1690 
1691 	RETURN_LONG(result);
1692 }
1693 /* }}} */
1694 
1695 /* {{{ Compares two numbers */
ZEND_FUNCTION(gmp_cmp)1696 ZEND_FUNCTION(gmp_cmp)
1697 {
1698 	zval *a_arg, *b_arg;
1699 
1700 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1701 		RETURN_THROWS();
1702 	}
1703 
1704 	gmp_cmp(return_value, a_arg, b_arg, /* is_operator */ false);
1705 }
1706 /* }}} */
1707 
1708 /* {{{ Gets the sign of the number */
ZEND_FUNCTION(gmp_sign)1709 ZEND_FUNCTION(gmp_sign)
1710 {
1711 	/* Can't use gmp_unary_opl here, because mpz_sgn is a macro */
1712 	zval *a_arg;
1713 	mpz_ptr gmpnum_a;
1714 	gmp_temp_t temp_a;
1715 
1716 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1717 		RETURN_THROWS();
1718 	}
1719 
1720 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1721 
1722 	RETVAL_LONG(mpz_sgn(gmpnum_a));
1723 	FREE_GMP_TEMP(temp_a);
1724 }
1725 /* }}} */
1726 
gmp_init_random(void)1727 static void gmp_init_random(void)
1728 {
1729 	if (!GMPG(rand_initialized)) {
1730 		/* Initialize */
1731 		gmp_randinit_mt(GMPG(rand_state));
1732 		/* Seed */
1733 		zend_long seed = 0;
1734 		if (php_random_bytes_silent(&seed, sizeof(zend_long)) == FAILURE) {
1735 			seed = GENERATE_SEED();
1736 		}
1737 		gmp_randseed_ui(GMPG(rand_state), seed);
1738 
1739 		GMPG(rand_initialized) = 1;
1740 	}
1741 }
1742 
1743 /* {{{ Seed the RNG */
ZEND_FUNCTION(gmp_random_seed)1744 ZEND_FUNCTION(gmp_random_seed)
1745 {
1746 	zval *seed;
1747 	gmp_temp_t temp_a;
1748 
1749 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &seed) == FAILURE) {
1750 		RETURN_THROWS();
1751 	}
1752 
1753 	gmp_init_random();
1754 
1755 	if (Z_TYPE_P(seed) == IS_LONG && Z_LVAL_P(seed) >= 0) {
1756 		gmp_randseed_ui(GMPG(rand_state), Z_LVAL_P(seed));
1757 	}
1758 	else {
1759 		mpz_ptr gmpnum_seed;
1760 
1761 		FETCH_GMP_ZVAL(gmpnum_seed, seed, temp_a, 1);
1762 
1763 		gmp_randseed(GMPG(rand_state), gmpnum_seed);
1764 
1765 		FREE_GMP_TEMP(temp_a);
1766 	}
1767 }
1768 /* }}} */
1769 
1770 /* {{{ Gets a random number in the range 0 to (2 ** n) - 1 */
ZEND_FUNCTION(gmp_random_bits)1771 ZEND_FUNCTION(gmp_random_bits)
1772 {
1773 	zend_long bits;
1774 	mpz_ptr gmpnum_result;
1775 
1776 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &bits) == FAILURE) {
1777 		RETURN_THROWS();
1778 	}
1779 
1780 	if (bits <= 0) {
1781 		zend_argument_value_error(1, "must be greater than or equal to 1");
1782 		RETURN_THROWS();
1783 	}
1784 
1785 	INIT_GMP_RETVAL(gmpnum_result);
1786 	gmp_init_random();
1787 
1788 	mpz_urandomb(gmpnum_result, GMPG(rand_state), bits);
1789 }
1790 /* }}} */
1791 
1792 /* {{{ Gets a random number in the range min to max */
ZEND_FUNCTION(gmp_random_range)1793 ZEND_FUNCTION(gmp_random_range)
1794 {
1795 	zval *min_arg, *max_arg;
1796 	mpz_ptr gmpnum_max, gmpnum_result;
1797 	mpz_t gmpnum_range;
1798 	gmp_temp_t temp_a, temp_b;
1799 
1800 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &min_arg, &max_arg) == FAILURE) {
1801 		RETURN_THROWS();
1802 	}
1803 
1804 	gmp_init_random();
1805 
1806 	FETCH_GMP_ZVAL(gmpnum_max, max_arg, temp_a, 2);
1807 
1808 	if (Z_TYPE_P(min_arg) == IS_LONG && Z_LVAL_P(min_arg) >= 0) {
1809 		if (mpz_cmp_ui(gmpnum_max, Z_LVAL_P(min_arg)) <= 0) {
1810 			FREE_GMP_TEMP(temp_a);
1811 			zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
1812 			RETURN_THROWS();
1813 		}
1814 
1815 		INIT_GMP_RETVAL(gmpnum_result);
1816 		mpz_init(gmpnum_range);
1817 
1818 		if (Z_LVAL_P(min_arg) != 0) {
1819 			mpz_sub_ui(gmpnum_range, gmpnum_max, Z_LVAL_P(min_arg) - 1);
1820 		} else {
1821 			mpz_add_ui(gmpnum_range, gmpnum_max, 1);
1822 		}
1823 
1824 		mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1825 
1826 		if (Z_LVAL_P(min_arg) != 0) {
1827 			mpz_add_ui(gmpnum_result, gmpnum_result, Z_LVAL_P(min_arg));
1828 		}
1829 
1830 		mpz_clear(gmpnum_range);
1831 		FREE_GMP_TEMP(temp_a);
1832 	} else {
1833 		mpz_ptr gmpnum_min;
1834 
1835 		FETCH_GMP_ZVAL_DEP(gmpnum_min, min_arg, temp_b, temp_a, 1);
1836 
1837 		if (mpz_cmp(gmpnum_max, gmpnum_min) <= 0) {
1838 			FREE_GMP_TEMP(temp_b);
1839 			FREE_GMP_TEMP(temp_a);
1840 			zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
1841 			RETURN_THROWS();
1842 		}
1843 
1844 		INIT_GMP_RETVAL(gmpnum_result);
1845 		mpz_init(gmpnum_range);
1846 
1847 		mpz_sub(gmpnum_range, gmpnum_max, gmpnum_min);
1848 		mpz_add_ui(gmpnum_range, gmpnum_range, 1);
1849 		mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1850 		mpz_add(gmpnum_result, gmpnum_result, gmpnum_min);
1851 
1852 		mpz_clear(gmpnum_range);
1853 		FREE_GMP_TEMP(temp_b);
1854 		FREE_GMP_TEMP(temp_a);
1855 	}
1856 }
1857 /* }}} */
1858 
1859 /* {{{ Calculates logical AND of a and b */
ZEND_FUNCTION(gmp_and)1860 ZEND_FUNCTION(gmp_and)
1861 {
1862 	gmp_binary_op(mpz_and);
1863 }
1864 /* }}} */
1865 
1866 /* {{{ Calculates logical OR of a and b */
ZEND_FUNCTION(gmp_or)1867 ZEND_FUNCTION(gmp_or)
1868 {
1869 	gmp_binary_op(mpz_ior);
1870 }
1871 /* }}} */
1872 
1873 /* {{{ Calculates one's complement of a */
ZEND_FUNCTION(gmp_com)1874 ZEND_FUNCTION(gmp_com)
1875 {
1876 	gmp_unary_op(mpz_com);
1877 }
1878 /* }}} */
1879 
1880 /* {{{ Finds next prime of a */
ZEND_FUNCTION(gmp_nextprime)1881 ZEND_FUNCTION(gmp_nextprime)
1882 {
1883 	gmp_unary_op(mpz_nextprime);
1884 }
1885 /* }}} */
1886 
1887 /* {{{ Calculates logical exclusive OR of a and b */
ZEND_FUNCTION(gmp_xor)1888 ZEND_FUNCTION(gmp_xor)
1889 {
1890 	gmp_binary_op(mpz_xor);
1891 }
1892 /* }}} */
1893 
1894 /* {{{ Sets or clear bit in a */
ZEND_FUNCTION(gmp_setbit)1895 ZEND_FUNCTION(gmp_setbit)
1896 {
1897 	zval *a_arg;
1898 	zend_long index;
1899 	bool set = 1;
1900 	mpz_ptr gmpnum_a;
1901 
1902 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|b", &a_arg, gmp_ce, &index, &set) == FAILURE) {
1903 		RETURN_THROWS();
1904 	}
1905 
1906 	if (index < 0) {
1907 		zend_argument_value_error(2, "must be greater than or equal to 0");
1908 		RETURN_THROWS();
1909 	}
1910 	if (index / GMP_NUMB_BITS >= INT_MAX) {
1911 		zend_argument_value_error(2, "must be less than %d * %d", INT_MAX, GMP_NUMB_BITS);
1912 		RETURN_THROWS();
1913 	}
1914 
1915 	gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
1916 
1917 	if (set) {
1918 		mpz_setbit(gmpnum_a, index);
1919 	} else {
1920 		mpz_clrbit(gmpnum_a, index);
1921 	}
1922 }
1923 /* }}} */
1924 
1925 /* {{{ Clears bit in a */
ZEND_FUNCTION(gmp_clrbit)1926 ZEND_FUNCTION(gmp_clrbit)
1927 {
1928 	zval *a_arg;
1929 	zend_long index;
1930 	mpz_ptr gmpnum_a;
1931 
1932 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &a_arg, gmp_ce, &index) == FAILURE){
1933 		RETURN_THROWS();
1934 	}
1935 
1936 	if (index < 0) {
1937 		zend_argument_value_error(2, "must be greater than or equal to 0");
1938 		RETURN_THROWS();
1939 	}
1940 
1941 	gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
1942 	mpz_clrbit(gmpnum_a, index);
1943 }
1944 /* }}} */
1945 
1946 /* {{{ Tests if bit is set in a */
ZEND_FUNCTION(gmp_testbit)1947 ZEND_FUNCTION(gmp_testbit)
1948 {
1949 	zval *a_arg;
1950 	zend_long index;
1951 	mpz_ptr gmpnum_a;
1952 	gmp_temp_t temp_a;
1953 
1954 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &index) == FAILURE){
1955 		RETURN_THROWS();
1956 	}
1957 
1958 	if (index < 0) {
1959 		zend_argument_value_error(2, "must be greater than or equal to 0");
1960 		RETURN_THROWS();
1961 	}
1962 
1963 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1964 	RETVAL_BOOL(mpz_tstbit(gmpnum_a, index));
1965 	FREE_GMP_TEMP(temp_a);
1966 }
1967 /* }}} */
1968 
1969 /* {{{ Calculates the population count of a */
ZEND_FUNCTION(gmp_popcount)1970 ZEND_FUNCTION(gmp_popcount)
1971 {
1972 	gmp_unary_opl(mpz_popcount);
1973 }
1974 /* }}} */
1975 
1976 /* {{{ Calculates hamming distance between a and b */
ZEND_FUNCTION(gmp_hamdist)1977 ZEND_FUNCTION(gmp_hamdist)
1978 {
1979 	zval *a_arg, *b_arg;
1980 	mpz_ptr gmpnum_a, gmpnum_b;
1981 	gmp_temp_t temp_a, temp_b;
1982 
1983 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1984 		RETURN_THROWS();
1985 	}
1986 
1987 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1988 	FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1989 
1990 	RETVAL_LONG(mpz_hamdist(gmpnum_a, gmpnum_b));
1991 
1992 	FREE_GMP_TEMP(temp_a);
1993 	FREE_GMP_TEMP(temp_b);
1994 }
1995 /* }}} */
1996 
1997 /* {{{ Finds first zero bit */
ZEND_FUNCTION(gmp_scan0)1998 ZEND_FUNCTION(gmp_scan0)
1999 {
2000 	zval *a_arg;
2001 	mpz_ptr gmpnum_a;
2002 	gmp_temp_t temp_a;
2003 	zend_long start;
2004 
2005 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2006 		RETURN_THROWS();
2007 	}
2008 
2009 	if (start < 0) {
2010 		zend_argument_value_error(2, "must be greater than or equal to 0");
2011 		RETURN_THROWS();
2012 	}
2013 
2014 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2015 
2016 	RETVAL_LONG(mpz_scan0(gmpnum_a, start));
2017 	FREE_GMP_TEMP(temp_a);
2018 }
2019 /* }}} */
2020 
2021 /* {{{ Finds first non-zero bit */
ZEND_FUNCTION(gmp_scan1)2022 ZEND_FUNCTION(gmp_scan1)
2023 {
2024 	zval *a_arg;
2025 	mpz_ptr gmpnum_a;
2026 	gmp_temp_t temp_a;
2027 	zend_long start;
2028 
2029 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2030 		RETURN_THROWS();
2031 	}
2032 
2033 	if (start < 0) {
2034 		zend_argument_value_error(2, "must be greater than or equal to 0");
2035 		RETURN_THROWS();
2036 	}
2037 
2038 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2039 
2040 	RETVAL_LONG(mpz_scan1(gmpnum_a, start));
2041 	FREE_GMP_TEMP(temp_a);
2042 }
2043 /* }}} */
2044 
ZEND_METHOD(GMP,__construct)2045 ZEND_METHOD(GMP, __construct)
2046 {
2047 	zend_string *arg_str = NULL;
2048 	zend_long arg_l = 0;
2049 	zend_long base = 0;
2050 
2051 	ZEND_PARSE_PARAMETERS_START(0, 2)
2052 		Z_PARAM_OPTIONAL
2053 		Z_PARAM_STR_OR_LONG(arg_str, arg_l)
2054 		Z_PARAM_LONG(base)
2055 	ZEND_PARSE_PARAMETERS_END();
2056 
2057 	if (!gmp_verify_base(base, 2)) {
2058 		RETURN_THROWS();
2059 	}
2060 
2061 	return_value = ZEND_THIS;
2062 	mpz_ptr gmp_number = GET_GMP_FROM_ZVAL(ZEND_THIS);
2063 
2064 	if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
2065 		RETURN_THROWS();
2066 	}
2067 }
2068 
ZEND_METHOD(GMP,__serialize)2069 ZEND_METHOD(GMP, __serialize)
2070 {
2071 	ZEND_PARSE_PARAMETERS_NONE();
2072 
2073 	zval zv;
2074 	array_init(return_value);
2075 
2076 	mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(ZEND_THIS);
2077 	gmp_strval(&zv, gmpnum, 16);
2078 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
2079 
2080 	HashTable *props = Z_OBJ_P(ZEND_THIS)->properties;
2081 	if (props && zend_hash_num_elements(props) != 0) {
2082 		ZVAL_ARR(&zv, zend_proptable_to_symtable(
2083 			zend_std_get_properties(Z_OBJ_P(ZEND_THIS)), /* always duplicate */ 1));
2084 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
2085 	}
2086 }
2087 
ZEND_METHOD(GMP,__unserialize)2088 ZEND_METHOD(GMP, __unserialize)
2089 {
2090 	HashTable *data;
2091 
2092 	ZEND_PARSE_PARAMETERS_START(1, 1)
2093 		Z_PARAM_ARRAY_HT(data)
2094 	ZEND_PARSE_PARAMETERS_END();
2095 
2096 	zval *num = zend_hash_index_find(data, 0);
2097 	if (!num || Z_TYPE_P(num) != IS_STRING ||
2098 			convert_to_gmp(GET_GMP_FROM_ZVAL(ZEND_THIS), num, 16, 0) == FAILURE) {
2099 		zend_throw_exception(NULL, "Could not unserialize number", 0);
2100 		RETURN_THROWS();
2101 	}
2102 
2103 	zval *props = zend_hash_index_find(data, 1);
2104 	if (props) {
2105 		if (Z_TYPE_P(props) != IS_ARRAY) {
2106 			zend_throw_exception(NULL, "Could not unserialize properties", 0);
2107 			RETURN_THROWS();
2108 		}
2109 
2110 		object_properties_load(Z_OBJ_P(ZEND_THIS), Z_ARRVAL_P(props));
2111 	}
2112 }
2113