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