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