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