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