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