xref: /PHP-8.2/ext/gmp/gmp.c (revision 4ea85d40)
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 	intern->std.handlers = &gmp_object_handlers;
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,zend_uchar opcode)337 static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, zend_uchar 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(zend_uchar opcode,zval * result,zval * op1,zval * op2)374 static zend_result gmp_do_operation_ex(zend_uchar 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(zend_uchar opcode,zval * result,zval * op1,zval * op2)411 static zend_result gmp_do_operation(zend_uchar 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->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_type_name(val));
628 			} else {
629 				zend_argument_type_error(arg_pos,
630 					"must be of type GMP|string|int, %s given", zend_zval_type_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 		gmp_randseed_ui(GMPG(rand_state), GENERATE_SEED());
1733 
1734 		GMPG(rand_initialized) = 1;
1735 	}
1736 }
1737 
1738 /* {{{ Seed the RNG */
ZEND_FUNCTION(gmp_random_seed)1739 ZEND_FUNCTION(gmp_random_seed)
1740 {
1741 	zval *seed;
1742 	gmp_temp_t temp_a;
1743 
1744 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &seed) == FAILURE) {
1745 		RETURN_THROWS();
1746 	}
1747 
1748 	gmp_init_random();
1749 
1750 	if (Z_TYPE_P(seed) == IS_LONG && Z_LVAL_P(seed) >= 0) {
1751 		gmp_randseed_ui(GMPG(rand_state), Z_LVAL_P(seed));
1752 	}
1753 	else {
1754 		mpz_ptr gmpnum_seed;
1755 
1756 		FETCH_GMP_ZVAL(gmpnum_seed, seed, temp_a, 1);
1757 
1758 		gmp_randseed(GMPG(rand_state), gmpnum_seed);
1759 
1760 		FREE_GMP_TEMP(temp_a);
1761 	}
1762 }
1763 /* }}} */
1764 
1765 /* {{{ Gets a random number in the range 0 to (2 ** n) - 1 */
ZEND_FUNCTION(gmp_random_bits)1766 ZEND_FUNCTION(gmp_random_bits)
1767 {
1768 	zend_long bits;
1769 	mpz_ptr gmpnum_result;
1770 
1771 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &bits) == FAILURE) {
1772 		RETURN_THROWS();
1773 	}
1774 
1775 	if (bits <= 0) {
1776 		zend_argument_value_error(1, "must be greater than or equal to 1");
1777 		RETURN_THROWS();
1778 	}
1779 
1780 	INIT_GMP_RETVAL(gmpnum_result);
1781 	gmp_init_random();
1782 
1783 	mpz_urandomb(gmpnum_result, GMPG(rand_state), bits);
1784 }
1785 /* }}} */
1786 
1787 /* {{{ Gets a random number in the range min to max */
ZEND_FUNCTION(gmp_random_range)1788 ZEND_FUNCTION(gmp_random_range)
1789 {
1790 	zval *min_arg, *max_arg;
1791 	mpz_ptr gmpnum_max, gmpnum_result;
1792 	mpz_t gmpnum_range;
1793 	gmp_temp_t temp_a, temp_b;
1794 
1795 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &min_arg, &max_arg) == FAILURE) {
1796 		RETURN_THROWS();
1797 	}
1798 
1799 	gmp_init_random();
1800 
1801 	FETCH_GMP_ZVAL(gmpnum_max, max_arg, temp_a, 2);
1802 
1803 	if (Z_TYPE_P(min_arg) == IS_LONG && Z_LVAL_P(min_arg) >= 0) {
1804 		if (mpz_cmp_ui(gmpnum_max, Z_LVAL_P(min_arg)) <= 0) {
1805 			FREE_GMP_TEMP(temp_a);
1806 			zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
1807 			RETURN_THROWS();
1808 		}
1809 
1810 		INIT_GMP_RETVAL(gmpnum_result);
1811 		mpz_init(gmpnum_range);
1812 
1813 		if (Z_LVAL_P(min_arg) != 0) {
1814 			mpz_sub_ui(gmpnum_range, gmpnum_max, Z_LVAL_P(min_arg) - 1);
1815 		} else {
1816 			mpz_add_ui(gmpnum_range, gmpnum_max, 1);
1817 		}
1818 
1819 		mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1820 
1821 		if (Z_LVAL_P(min_arg) != 0) {
1822 			mpz_add_ui(gmpnum_result, gmpnum_result, Z_LVAL_P(min_arg));
1823 		}
1824 
1825 		mpz_clear(gmpnum_range);
1826 		FREE_GMP_TEMP(temp_a);
1827 	} else {
1828 		mpz_ptr gmpnum_min;
1829 
1830 		FETCH_GMP_ZVAL_DEP(gmpnum_min, min_arg, temp_b, temp_a, 1);
1831 
1832 		if (mpz_cmp(gmpnum_max, gmpnum_min) <= 0) {
1833 			FREE_GMP_TEMP(temp_b);
1834 			FREE_GMP_TEMP(temp_a);
1835 			zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
1836 			RETURN_THROWS();
1837 		}
1838 
1839 		INIT_GMP_RETVAL(gmpnum_result);
1840 		mpz_init(gmpnum_range);
1841 
1842 		mpz_sub(gmpnum_range, gmpnum_max, gmpnum_min);
1843 		mpz_add_ui(gmpnum_range, gmpnum_range, 1);
1844 		mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1845 		mpz_add(gmpnum_result, gmpnum_result, gmpnum_min);
1846 
1847 		mpz_clear(gmpnum_range);
1848 		FREE_GMP_TEMP(temp_b);
1849 		FREE_GMP_TEMP(temp_a);
1850 	}
1851 }
1852 /* }}} */
1853 
1854 /* {{{ Calculates logical AND of a and b */
ZEND_FUNCTION(gmp_and)1855 ZEND_FUNCTION(gmp_and)
1856 {
1857 	gmp_binary_op(mpz_and);
1858 }
1859 /* }}} */
1860 
1861 /* {{{ Calculates logical OR of a and b */
ZEND_FUNCTION(gmp_or)1862 ZEND_FUNCTION(gmp_or)
1863 {
1864 	gmp_binary_op(mpz_ior);
1865 }
1866 /* }}} */
1867 
1868 /* {{{ Calculates one's complement of a */
ZEND_FUNCTION(gmp_com)1869 ZEND_FUNCTION(gmp_com)
1870 {
1871 	gmp_unary_op(mpz_com);
1872 }
1873 /* }}} */
1874 
1875 /* {{{ Finds next prime of a */
ZEND_FUNCTION(gmp_nextprime)1876 ZEND_FUNCTION(gmp_nextprime)
1877 {
1878 	gmp_unary_op(mpz_nextprime);
1879 }
1880 /* }}} */
1881 
1882 /* {{{ Calculates logical exclusive OR of a and b */
ZEND_FUNCTION(gmp_xor)1883 ZEND_FUNCTION(gmp_xor)
1884 {
1885 	gmp_binary_op(mpz_xor);
1886 }
1887 /* }}} */
1888 
1889 /* {{{ Sets or clear bit in a */
ZEND_FUNCTION(gmp_setbit)1890 ZEND_FUNCTION(gmp_setbit)
1891 {
1892 	zval *a_arg;
1893 	zend_long index;
1894 	bool set = 1;
1895 	mpz_ptr gmpnum_a;
1896 
1897 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|b", &a_arg, gmp_ce, &index, &set) == FAILURE) {
1898 		RETURN_THROWS();
1899 	}
1900 
1901 	if (index < 0) {
1902 		zend_argument_value_error(2, "must be greater than or equal to 0");
1903 		RETURN_THROWS();
1904 	}
1905 	if (index / GMP_NUMB_BITS >= INT_MAX) {
1906 		zend_argument_value_error(2, "must be less than %d * %d", INT_MAX, GMP_NUMB_BITS);
1907 		RETURN_THROWS();
1908 	}
1909 
1910 	gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
1911 
1912 	if (set) {
1913 		mpz_setbit(gmpnum_a, index);
1914 	} else {
1915 		mpz_clrbit(gmpnum_a, index);
1916 	}
1917 }
1918 /* }}} */
1919 
1920 /* {{{ Clears bit in a */
ZEND_FUNCTION(gmp_clrbit)1921 ZEND_FUNCTION(gmp_clrbit)
1922 {
1923 	zval *a_arg;
1924 	zend_long index;
1925 	mpz_ptr gmpnum_a;
1926 
1927 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &a_arg, gmp_ce, &index) == FAILURE){
1928 		RETURN_THROWS();
1929 	}
1930 
1931 	if (index < 0) {
1932 		zend_argument_value_error(2, "must be greater than or equal to 0");
1933 		RETURN_THROWS();
1934 	}
1935 
1936 	gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
1937 	mpz_clrbit(gmpnum_a, index);
1938 }
1939 /* }}} */
1940 
1941 /* {{{ Tests if bit is set in a */
ZEND_FUNCTION(gmp_testbit)1942 ZEND_FUNCTION(gmp_testbit)
1943 {
1944 	zval *a_arg;
1945 	zend_long index;
1946 	mpz_ptr gmpnum_a;
1947 	gmp_temp_t temp_a;
1948 
1949 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &index) == FAILURE){
1950 		RETURN_THROWS();
1951 	}
1952 
1953 	if (index < 0) {
1954 		zend_argument_value_error(2, "must be greater than or equal to 0");
1955 		RETURN_THROWS();
1956 	}
1957 
1958 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1959 	RETVAL_BOOL(mpz_tstbit(gmpnum_a, index));
1960 	FREE_GMP_TEMP(temp_a);
1961 }
1962 /* }}} */
1963 
1964 /* {{{ Calculates the population count of a */
ZEND_FUNCTION(gmp_popcount)1965 ZEND_FUNCTION(gmp_popcount)
1966 {
1967 	gmp_unary_opl(mpz_popcount);
1968 }
1969 /* }}} */
1970 
1971 /* {{{ Calculates hamming distance between a and b */
ZEND_FUNCTION(gmp_hamdist)1972 ZEND_FUNCTION(gmp_hamdist)
1973 {
1974 	zval *a_arg, *b_arg;
1975 	mpz_ptr gmpnum_a, gmpnum_b;
1976 	gmp_temp_t temp_a, temp_b;
1977 
1978 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1979 		RETURN_THROWS();
1980 	}
1981 
1982 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1983 	FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1984 
1985 	RETVAL_LONG(mpz_hamdist(gmpnum_a, gmpnum_b));
1986 
1987 	FREE_GMP_TEMP(temp_a);
1988 	FREE_GMP_TEMP(temp_b);
1989 }
1990 /* }}} */
1991 
1992 /* {{{ Finds first zero bit */
ZEND_FUNCTION(gmp_scan0)1993 ZEND_FUNCTION(gmp_scan0)
1994 {
1995 	zval *a_arg;
1996 	mpz_ptr gmpnum_a;
1997 	gmp_temp_t temp_a;
1998 	zend_long start;
1999 
2000 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2001 		RETURN_THROWS();
2002 	}
2003 
2004 	if (start < 0) {
2005 		zend_argument_value_error(2, "must be greater than or equal to 0");
2006 		RETURN_THROWS();
2007 	}
2008 
2009 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2010 
2011 	RETVAL_LONG(mpz_scan0(gmpnum_a, start));
2012 	FREE_GMP_TEMP(temp_a);
2013 }
2014 /* }}} */
2015 
2016 /* {{{ Finds first non-zero bit */
ZEND_FUNCTION(gmp_scan1)2017 ZEND_FUNCTION(gmp_scan1)
2018 {
2019 	zval *a_arg;
2020 	mpz_ptr gmpnum_a;
2021 	gmp_temp_t temp_a;
2022 	zend_long start;
2023 
2024 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2025 		RETURN_THROWS();
2026 	}
2027 
2028 	if (start < 0) {
2029 		zend_argument_value_error(2, "must be greater than or equal to 0");
2030 		RETURN_THROWS();
2031 	}
2032 
2033 	FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2034 
2035 	RETVAL_LONG(mpz_scan1(gmpnum_a, start));
2036 	FREE_GMP_TEMP(temp_a);
2037 }
2038 /* }}} */
2039 
ZEND_METHOD(GMP,__construct)2040 ZEND_METHOD(GMP, __construct)
2041 {
2042 	zend_string *arg_str = NULL;
2043 	zend_long arg_l = 0;
2044 	zend_long base = 0;
2045 
2046 	ZEND_PARSE_PARAMETERS_START(0, 2)
2047 		Z_PARAM_OPTIONAL
2048 		Z_PARAM_STR_OR_LONG(arg_str, arg_l)
2049 		Z_PARAM_LONG(base)
2050 	ZEND_PARSE_PARAMETERS_END();
2051 
2052 	if (!gmp_verify_base(base, 2)) {
2053 		RETURN_THROWS();
2054 	}
2055 
2056 	return_value = ZEND_THIS;
2057 	mpz_ptr gmp_number = GET_GMP_FROM_ZVAL(ZEND_THIS);
2058 
2059 	if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
2060 		RETURN_THROWS();
2061 	}
2062 }
2063 
ZEND_METHOD(GMP,__serialize)2064 ZEND_METHOD(GMP, __serialize)
2065 {
2066 	ZEND_PARSE_PARAMETERS_NONE();
2067 
2068 	zval zv;
2069 	array_init(return_value);
2070 
2071 	mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(ZEND_THIS);
2072 	gmp_strval(&zv, gmpnum, 16);
2073 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
2074 
2075 	HashTable *props = Z_OBJ_P(ZEND_THIS)->properties;
2076 	if (props && zend_hash_num_elements(props) != 0) {
2077 		ZVAL_ARR(&zv, zend_proptable_to_symtable(
2078 			zend_std_get_properties(Z_OBJ_P(ZEND_THIS)), /* always duplicate */ 1));
2079 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
2080 	}
2081 }
2082 
ZEND_METHOD(GMP,__unserialize)2083 ZEND_METHOD(GMP, __unserialize)
2084 {
2085 	HashTable *data;
2086 
2087 	ZEND_PARSE_PARAMETERS_START(1, 1)
2088 		Z_PARAM_ARRAY_HT(data)
2089 	ZEND_PARSE_PARAMETERS_END();
2090 
2091 	zval *num = zend_hash_index_find(data, 0);
2092 	if (!num || Z_TYPE_P(num) != IS_STRING ||
2093 			convert_to_gmp(GET_GMP_FROM_ZVAL(ZEND_THIS), num, 16, 0) == FAILURE) {
2094 		zend_throw_exception(NULL, "Could not unserialize number", 0);
2095 		RETURN_THROWS();
2096 	}
2097 
2098 	zval *props = zend_hash_index_find(data, 1);
2099 	if (props) {
2100 		if (Z_TYPE_P(props) != IS_ARRAY) {
2101 			zend_throw_exception(NULL, "Could not unserialize properties", 0);
2102 			RETURN_THROWS();
2103 		}
2104 
2105 		object_properties_load(Z_OBJ_P(ZEND_THIS), Z_ARRVAL_P(props));
2106 	}
2107 }
2108