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