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