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