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 size_t num_len = ZSTR_LEN(val);
587 while (isspace(*num_str)) {
588 ++num_str;
589 --num_len;
590 }
591
592 if (num_len >= 2 && num_str[0] == '0') {
593 if ((base == 0 || base == 16) && (num_str[1] == 'x' || num_str[1] == 'X')) {
594 base = 16;
595 skip_lead = true;
596 } else if ((base == 0 || base == 8) && (num_str[1] == 'o' || num_str[1] == 'O')) {
597 base = 8;
598 skip_lead = true;
599 } else if ((base == 0 || base == 2) && (num_str[1] == 'b' || num_str[1] == 'B')) {
600 base = 2;
601 skip_lead = true;
602 }
603 }
604
605 int gmp_ret = mpz_set_str(gmp_number, (skip_lead ? &num_str[2] : num_str), (int) base);
606 if (-1 == gmp_ret) {
607 if (arg_pos == 0) {
608 zend_value_error("Number is not an integer string");
609 } else {
610 zend_argument_value_error(arg_pos, "is not an integer string");
611 }
612 return FAILURE;
613 }
614
615 return SUCCESS;
616 }
617
618 /* {{{ convert_to_gmp
619 * Convert zval to be gmp number */
convert_to_gmp(mpz_t gmpnumber,zval * val,zend_long base,uint32_t arg_pos)620 static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, uint32_t arg_pos)
621 {
622 switch (Z_TYPE_P(val)) {
623 case IS_LONG:
624 mpz_set_si(gmpnumber, Z_LVAL_P(val));
625 return SUCCESS;
626 case IS_STRING: {
627 return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos);
628 }
629 default: {
630 zend_long lval;
631 if (!zend_parse_arg_long_slow(val, &lval, arg_pos)) {
632 if (arg_pos == 0) {
633 zend_type_error(
634 "Number must be of type GMP|string|int, %s given", zend_zval_value_name(val));
635 } else {
636 zend_argument_type_error(arg_pos,
637 "must be of type GMP|string|int, %s given", zend_zval_value_name(val));
638 }
639 return FAILURE;
640 }
641
642 mpz_set_si(gmpnumber, lval);
643 return SUCCESS;
644 }
645 }
646 }
647 /* }}} */
648
gmp_strval(zval * result,mpz_t gmpnum,int base)649 static void gmp_strval(zval *result, mpz_t gmpnum, int base) /* {{{ */
650 {
651 size_t num_len;
652 zend_string *str;
653
654 num_len = mpz_sizeinbase(gmpnum, abs(base));
655 if (mpz_sgn(gmpnum) < 0) {
656 num_len++;
657 }
658
659 str = zend_string_alloc(num_len, 0);
660 mpz_get_str(ZSTR_VAL(str), base, gmpnum);
661
662 /*
663 * From GMP documentation for mpz_sizeinbase():
664 * The returned value will be exact or 1 too big. If base is a power of
665 * 2, the returned value will always be exact.
666 *
667 * So let's check to see if we already have a \0 byte...
668 */
669
670 if (ZSTR_VAL(str)[ZSTR_LEN(str) - 1] == '\0') {
671 ZSTR_LEN(str)--;
672 } else {
673 ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
674 }
675
676 ZVAL_NEW_STR(result, str);
677 }
678 /* }}} */
679
gmp_cmp(zval * return_value,zval * a_arg,zval * b_arg,bool is_operator)680 static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operator) /* {{{ */
681 {
682 mpz_ptr gmpnum_a, gmpnum_b;
683 gmp_temp_t temp_a, temp_b;
684 bool use_si = 0;
685 zend_long res;
686
687 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
688
689 if (Z_TYPE_P(b_arg) == IS_LONG) {
690 use_si = 1;
691 temp_b.is_used = 0;
692 } else {
693 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
694 }
695
696 if (use_si) {
697 res = mpz_cmp_si(gmpnum_a, Z_LVAL_P(b_arg));
698 } else {
699 res = mpz_cmp(gmpnum_a, gmpnum_b);
700 }
701
702 FREE_GMP_TEMP(temp_a);
703 FREE_GMP_TEMP(temp_b);
704
705 RETURN_LONG(res);
706 }
707 /* }}} */
708
709 /* {{{ gmp_zval_binary_ui_op
710 Execute GMP binary operation.
711 */
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)712 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)
713 {
714 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
715 gmp_temp_t temp_a, temp_b;
716
717 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
718
719 if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
720 gmpnum_b = NULL;
721 temp_b.is_used = 0;
722 } else {
723 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
724 }
725
726 if (check_b_zero) {
727 int b_is_zero = 0;
728 if (!gmpnum_b) {
729 b_is_zero = (Z_LVAL_P(b_arg) == 0);
730 } else {
731 b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
732 }
733
734 if (b_is_zero) {
735 if ((gmp_binary_op_t) mpz_mod == gmp_op) {
736 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
737 } else {
738 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
739 }
740 FREE_GMP_TEMP(temp_a);
741 FREE_GMP_TEMP(temp_b);
742 RETURN_THROWS();
743 }
744 }
745
746 INIT_GMP_RETVAL(gmpnum_result);
747
748 if (!gmpnum_b) {
749 gmp_ui_op(gmpnum_result, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
750 } else {
751 gmp_op(gmpnum_result, gmpnum_a, gmpnum_b);
752 }
753
754 FREE_GMP_TEMP(temp_a);
755 FREE_GMP_TEMP(temp_b);
756 }
757 /* }}} */
758
759 /* {{{ gmp_zval_binary_ui_op2
760 Execute GMP binary operation which returns 2 values.
761 */
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)762 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)
763 {
764 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result1, gmpnum_result2;
765 gmp_temp_t temp_a, temp_b;
766 zval result1, result2;
767
768 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
769
770 if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
771 gmpnum_b = NULL;
772 temp_b.is_used = 0;
773 } else {
774 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
775 }
776
777 if (check_b_zero) {
778 int b_is_zero = 0;
779 if (!gmpnum_b) {
780 b_is_zero = (Z_LVAL_P(b_arg) == 0);
781 } else {
782 b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
783 }
784
785 if (b_is_zero) {
786 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
787 FREE_GMP_TEMP(temp_a);
788 FREE_GMP_TEMP(temp_b);
789 RETURN_THROWS();
790 }
791 }
792
793 gmp_create(&result1, &gmpnum_result1);
794 gmp_create(&result2, &gmpnum_result2);
795
796 array_init(return_value);
797 add_next_index_zval(return_value, &result1);
798 add_next_index_zval(return_value, &result2);
799
800 if (!gmpnum_b) {
801 gmp_ui_op(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
802 } else {
803 gmp_op(gmpnum_result1, gmpnum_result2, gmpnum_a, gmpnum_b);
804 }
805
806 FREE_GMP_TEMP(temp_a);
807 FREE_GMP_TEMP(temp_b);
808 }
809 /* }}} */
810
811 /* {{{ _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)812 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)
813 {
814 zval *a_arg, *b_arg;
815
816 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
817 RETURN_THROWS();
818 }
819
820 gmp_zval_binary_ui_op(
821 return_value, a_arg, b_arg, gmp_op, gmp_ui_op, check_b_zero, /* is_operator */ false);
822 }
823 /* }}} */
824
825 /* Unary operations */
826
827 /* {{{ gmp_zval_unary_op */
gmp_zval_unary_op(zval * return_value,zval * a_arg,gmp_unary_op_t gmp_op)828 static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op)
829 {
830 mpz_ptr gmpnum_a, gmpnum_result;
831 gmp_temp_t temp_a;
832
833 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
834
835 INIT_GMP_RETVAL(gmpnum_result);
836 gmp_op(gmpnum_result, gmpnum_a);
837
838 FREE_GMP_TEMP(temp_a);
839 }
840 /* }}} */
841
842 /* {{{ _gmp_unary_op */
_gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS,gmp_unary_op_t gmp_op)843 static inline void _gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_op_t gmp_op)
844 {
845 zval *a_arg;
846
847 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
848 RETURN_THROWS();
849 }
850
851 gmp_zval_unary_op(return_value, a_arg, gmp_op);
852 }
853 /* }}} */
854
855 /* {{{ _gmp_unary_opl */
_gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS,gmp_unary_opl_t gmp_op)856 static inline void _gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_opl_t gmp_op)
857 {
858 zval *a_arg;
859 mpz_ptr gmpnum_a;
860 gmp_temp_t temp_a;
861
862 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
863 RETURN_THROWS();
864 }
865
866 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
867 RETVAL_LONG(gmp_op(gmpnum_a));
868 FREE_GMP_TEMP(temp_a);
869 }
870 /* }}} */
871
gmp_verify_base(zend_long base,uint32_t arg_num)872 static bool gmp_verify_base(zend_long base, uint32_t arg_num)
873 {
874 if (base && (base < 2 || base > GMP_MAX_BASE)) {
875 zend_argument_value_error(arg_num, "must be 0 or between 2 and %d", GMP_MAX_BASE);
876 return false;
877 }
878
879 return true;
880 }
881
gmp_initialize_number(mpz_ptr gmp_number,const zend_string * arg_str,zend_long arg_l,zend_long base)882 static zend_result gmp_initialize_number(mpz_ptr gmp_number, const zend_string *arg_str, zend_long arg_l, zend_long base)
883 {
884 if (arg_str) {
885 return convert_zstr_to_gmp(gmp_number, arg_str, base, 1);
886 }
887
888 mpz_set_si(gmp_number, arg_l);
889 return SUCCESS;
890 }
891
892 /* {{{ Initializes GMP number */
ZEND_FUNCTION(gmp_init)893 ZEND_FUNCTION(gmp_init)
894 {
895 mpz_ptr gmp_number;
896 zend_string *arg_str = NULL;
897 zend_long arg_l = 0;
898 zend_long base = 0;
899
900 ZEND_PARSE_PARAMETERS_START(1, 2)
901 Z_PARAM_STR_OR_LONG(arg_str, arg_l)
902 Z_PARAM_OPTIONAL
903 Z_PARAM_LONG(base)
904 ZEND_PARSE_PARAMETERS_END();
905
906 if (!gmp_verify_base(base, 2)) {
907 RETURN_THROWS();
908 }
909
910 INIT_GMP_RETVAL(gmp_number);
911
912 if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
913 RETURN_THROWS();
914 }
915 }
916 /* }}} */
917
gmp_import_export_validate(zend_long size,zend_long options,int * order,int * endian)918 int gmp_import_export_validate(zend_long size, zend_long options, int *order, int *endian)
919 {
920 if (size < 1) {
921 /* size argument is in second position */
922 zend_argument_value_error(2, "must be greater than or equal to 1");
923 return FAILURE;
924 }
925
926 switch (options & (GMP_LSW_FIRST | GMP_MSW_FIRST)) {
927 case GMP_LSW_FIRST:
928 *order = -1;
929 break;
930 case GMP_MSW_FIRST:
931 case 0: /* default */
932 *order = 1;
933 break;
934 default:
935 /* options argument is in second position */
936 zend_argument_value_error(3, "cannot use multiple word order options");
937 return FAILURE;
938 }
939
940 switch (options & (GMP_LITTLE_ENDIAN | GMP_BIG_ENDIAN | GMP_NATIVE_ENDIAN)) {
941 case GMP_LITTLE_ENDIAN:
942 *endian = -1;
943 break;
944 case GMP_BIG_ENDIAN:
945 *endian = 1;
946 break;
947 case GMP_NATIVE_ENDIAN:
948 case 0: /* default */
949 *endian = 0;
950 break;
951 default:
952 /* options argument is in second position */
953 zend_argument_value_error(3, "cannot use multiple endian options");
954 return FAILURE;
955 }
956
957 return SUCCESS;
958 }
959
960 /* {{{ Imports a GMP number from a binary string */
ZEND_FUNCTION(gmp_import)961 ZEND_FUNCTION(gmp_import)
962 {
963 char *data;
964 size_t data_len;
965 zend_long size = 1;
966 zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
967 int order, endian;
968 mpz_ptr gmpnumber;
969
970 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &data, &data_len, &size, &options) == FAILURE) {
971 RETURN_THROWS();
972 }
973
974 if (gmp_import_export_validate(size, options, &order, &endian) == FAILURE) {
975 RETURN_THROWS();
976 }
977
978 if ((data_len % size) != 0) {
979 zend_argument_value_error(1, "must be a multiple of argument #2 ($word_size)");
980 RETURN_THROWS();
981 }
982
983 INIT_GMP_RETVAL(gmpnumber);
984
985 mpz_import(gmpnumber, data_len / size, order, size, endian, 0, data);
986 }
987 /* }}} */
988
989 /* {{{ Exports a GMP number to a binary string */
ZEND_FUNCTION(gmp_export)990 ZEND_FUNCTION(gmp_export)
991 {
992 zval *gmpnumber_arg;
993 zend_long size = 1;
994 zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
995 int order, endian;
996 mpz_ptr gmpnumber;
997 gmp_temp_t temp_a;
998
999 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|ll", &gmpnumber_arg, &size, &options) == FAILURE) {
1000 RETURN_THROWS();
1001 }
1002
1003 if (gmp_import_export_validate(size, options, &order, &endian) == FAILURE) {
1004 RETURN_THROWS();
1005 }
1006
1007 FETCH_GMP_ZVAL(gmpnumber, gmpnumber_arg, temp_a, 1);
1008
1009 if (mpz_sgn(gmpnumber) == 0) {
1010 RETVAL_EMPTY_STRING();
1011 } else {
1012 size_t bits_per_word = size * 8;
1013 size_t count = (mpz_sizeinbase(gmpnumber, 2) + bits_per_word - 1) / bits_per_word;
1014
1015 zend_string *out_string = zend_string_safe_alloc(count, size, 0, 0);
1016 mpz_export(ZSTR_VAL(out_string), NULL, order, size, endian, 0, gmpnumber);
1017 ZSTR_VAL(out_string)[ZSTR_LEN(out_string)] = '\0';
1018
1019 RETVAL_NEW_STR(out_string);
1020 }
1021
1022 FREE_GMP_TEMP(temp_a);
1023 }
1024 /* }}} */
1025
1026 /* {{{ Gets signed long value of GMP number */
ZEND_FUNCTION(gmp_intval)1027 ZEND_FUNCTION(gmp_intval)
1028 {
1029 zval *gmpnumber_arg;
1030 mpz_ptr gmpnum;
1031 gmp_temp_t temp_a;
1032
1033 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &gmpnumber_arg) == FAILURE){
1034 RETURN_THROWS();
1035 }
1036
1037 FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a, 1);
1038 RETVAL_LONG(mpz_get_si(gmpnum));
1039 FREE_GMP_TEMP(temp_a);
1040 }
1041 /* }}} */
1042
1043 /* {{{ Gets string representation of GMP number */
ZEND_FUNCTION(gmp_strval)1044 ZEND_FUNCTION(gmp_strval)
1045 {
1046 zval *gmpnumber_arg;
1047 zend_long base = 10;
1048 mpz_ptr gmpnum;
1049 gmp_temp_t temp_a;
1050
1051 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &base) == FAILURE) {
1052 RETURN_THROWS();
1053 }
1054
1055 /* Although the maximum base in general in GMP is 62, mpz_get_str()
1056 * is explicitly limited to -36 when dealing with negative bases. */
1057 if ((base < 2 && base > -2) || base > GMP_MAX_BASE || base < -36) {
1058 zend_argument_value_error(2, "must be between 2 and %d, or -2 and -36", GMP_MAX_BASE);
1059 RETURN_THROWS();
1060 }
1061
1062 FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a, 1);
1063
1064 gmp_strval(return_value, gmpnum, (int)base);
1065
1066 FREE_GMP_TEMP(temp_a);
1067 }
1068 /* }}} */
1069
1070 /* {{{ Add a and b */
ZEND_FUNCTION(gmp_add)1071 ZEND_FUNCTION(gmp_add)
1072 {
1073 gmp_binary_ui_op(mpz_add, mpz_add_ui);
1074 }
1075 /* }}} */
1076
1077 /* {{{ Subtract b from a */
ZEND_FUNCTION(gmp_sub)1078 ZEND_FUNCTION(gmp_sub)
1079 {
1080 gmp_binary_ui_op(mpz_sub, mpz_sub_ui);
1081 }
1082 /* }}} */
1083
1084 /* {{{ Multiply a and b */
ZEND_FUNCTION(gmp_mul)1085 ZEND_FUNCTION(gmp_mul)
1086 {
1087 gmp_binary_ui_op(mpz_mul, mpz_mul_ui);
1088 }
1089 /* }}} */
1090
1091 /* {{{ Divide a by b, returns quotient and reminder */
ZEND_FUNCTION(gmp_div_qr)1092 ZEND_FUNCTION(gmp_div_qr)
1093 {
1094 zval *a_arg, *b_arg;
1095 zend_long round = GMP_ROUND_ZERO;
1096
1097 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1098 RETURN_THROWS();
1099 }
1100
1101 switch (round) {
1102 case GMP_ROUND_ZERO:
1103 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_tdiv_qr, mpz_tdiv_qr_ui, 1);
1104 break;
1105 case GMP_ROUND_PLUSINF:
1106 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_cdiv_qr, mpz_cdiv_qr_ui, 1);
1107 break;
1108 case GMP_ROUND_MINUSINF:
1109 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_fdiv_qr, mpz_fdiv_qr_ui, 1);
1110 break;
1111 default:
1112 zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1113 RETURN_THROWS();
1114 }
1115 }
1116 /* }}} */
1117
1118 /* {{{ Divide a by b, returns reminder only */
ZEND_FUNCTION(gmp_div_r)1119 ZEND_FUNCTION(gmp_div_r)
1120 {
1121 zval *a_arg, *b_arg;
1122 zend_long round = GMP_ROUND_ZERO;
1123
1124 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1125 RETURN_THROWS();
1126 }
1127
1128 switch (round) {
1129 case GMP_ROUND_ZERO:
1130 gmp_zval_binary_ui_op(
1131 return_value, a_arg, b_arg, mpz_tdiv_r, gmp_mpz_tdiv_r_ui, 1, /* is_operator */ false);
1132 break;
1133 case GMP_ROUND_PLUSINF:
1134 gmp_zval_binary_ui_op(
1135 return_value, a_arg, b_arg, mpz_cdiv_r, gmp_mpz_cdiv_r_ui, 1, /* is_operator */ false);
1136 break;
1137 case GMP_ROUND_MINUSINF:
1138 gmp_zval_binary_ui_op(
1139 return_value, a_arg, b_arg, mpz_fdiv_r, gmp_mpz_fdiv_r_ui, 1, /* is_operator */ false);
1140 break;
1141 default:
1142 zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1143 RETURN_THROWS();
1144 }
1145 }
1146 /* }}} */
1147
1148 /* {{{ Divide a by b, returns quotient only */
ZEND_FUNCTION(gmp_div_q)1149 ZEND_FUNCTION(gmp_div_q)
1150 {
1151 zval *a_arg, *b_arg;
1152 zend_long round = GMP_ROUND_ZERO;
1153
1154 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1155 RETURN_THROWS();
1156 }
1157
1158 switch (round) {
1159 case GMP_ROUND_ZERO:
1160 gmp_zval_binary_ui_op(
1161 return_value, a_arg, b_arg, mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1, /* is_operator */ false);
1162 break;
1163 case GMP_ROUND_PLUSINF:
1164 gmp_zval_binary_ui_op(
1165 return_value, a_arg, b_arg, mpz_cdiv_q, gmp_mpz_cdiv_q_ui, 1, /* is_operator */ false);
1166 break;
1167 case GMP_ROUND_MINUSINF:
1168 gmp_zval_binary_ui_op(
1169 return_value, a_arg, b_arg, mpz_fdiv_q, gmp_mpz_fdiv_q_ui, 1, /* is_operator */ false);
1170 break;
1171 default:
1172 zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1173 RETURN_THROWS();
1174 }
1175
1176 }
1177 /* }}} */
1178
1179 /* {{{ Computes a modulo b */
ZEND_FUNCTION(gmp_mod)1180 ZEND_FUNCTION(gmp_mod)
1181 {
1182 gmp_binary_ui_op_no_zero(mpz_mod, gmp_mpz_mod_ui);
1183 }
1184 /* }}} */
1185
1186 /* {{{ Divide a by b using exact division algorithm */
ZEND_FUNCTION(gmp_divexact)1187 ZEND_FUNCTION(gmp_divexact)
1188 {
1189 gmp_binary_ui_op_no_zero(mpz_divexact, NULL);
1190 }
1191 /* }}} */
1192
1193 /* {{{ Negates a number */
ZEND_FUNCTION(gmp_neg)1194 ZEND_FUNCTION(gmp_neg)
1195 {
1196 gmp_unary_op(mpz_neg);
1197 }
1198 /* }}} */
1199
1200 /* {{{ Calculates absolute value */
ZEND_FUNCTION(gmp_abs)1201 ZEND_FUNCTION(gmp_abs)
1202 {
1203 gmp_unary_op(mpz_abs);
1204 }
1205 /* }}} */
1206
1207 /* {{{ Calculates factorial function */
ZEND_FUNCTION(gmp_fact)1208 ZEND_FUNCTION(gmp_fact)
1209 {
1210 zval *a_arg;
1211 mpz_ptr gmpnum_result;
1212
1213 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1214 RETURN_THROWS();
1215 }
1216
1217 if (Z_TYPE_P(a_arg) == IS_LONG) {
1218 if (Z_LVAL_P(a_arg) < 0) {
1219 zend_argument_value_error(1, "must be greater than or equal to 0");
1220 RETURN_THROWS();
1221 }
1222 } else {
1223 mpz_ptr gmpnum;
1224 gmp_temp_t temp_a;
1225
1226 FETCH_GMP_ZVAL(gmpnum, a_arg, temp_a, 1);
1227 FREE_GMP_TEMP(temp_a);
1228
1229 if (mpz_sgn(gmpnum) < 0) {
1230 zend_argument_value_error(1, "must be greater than or equal to 0");
1231 RETURN_THROWS();
1232 }
1233 }
1234
1235 INIT_GMP_RETVAL(gmpnum_result);
1236 mpz_fac_ui(gmpnum_result, zval_get_long(a_arg));
1237 }
1238 /* }}} */
1239
1240 /* {{{ Calculates binomial coefficient */
ZEND_FUNCTION(gmp_binomial)1241 ZEND_FUNCTION(gmp_binomial)
1242 {
1243 zval *n_arg;
1244 zend_long k;
1245 mpz_ptr gmpnum_result;
1246
1247 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &n_arg, &k) == FAILURE) {
1248 RETURN_THROWS();
1249 }
1250
1251 if (k < 0) {
1252 zend_argument_value_error(2, "must be greater than or equal to 0");
1253 RETURN_THROWS();
1254 }
1255
1256 INIT_GMP_RETVAL(gmpnum_result);
1257 if (Z_TYPE_P(n_arg) == IS_LONG && Z_LVAL_P(n_arg) >= 0) {
1258 mpz_bin_uiui(gmpnum_result, (gmp_ulong) Z_LVAL_P(n_arg), (gmp_ulong) k);
1259 } else {
1260 mpz_ptr gmpnum_n;
1261 gmp_temp_t temp_n;
1262 FETCH_GMP_ZVAL(gmpnum_n, n_arg, temp_n, 1);
1263 mpz_bin_ui(gmpnum_result, gmpnum_n, (gmp_ulong) k);
1264 FREE_GMP_TEMP(temp_n);
1265 }
1266 }
1267 /* }}} */
1268
1269 /* {{{ Raise base to power exp */
ZEND_FUNCTION(gmp_pow)1270 ZEND_FUNCTION(gmp_pow)
1271 {
1272 zval *base_arg;
1273 mpz_ptr gmpnum_result;
1274 gmp_temp_t temp_base;
1275 zend_long exp;
1276
1277 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &base_arg, &exp) == FAILURE) {
1278 RETURN_THROWS();
1279 }
1280
1281 if (exp < 0) {
1282 zend_argument_value_error(2, "must be greater than or equal to 0");
1283 RETURN_THROWS();
1284 }
1285
1286 if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) {
1287 INIT_GMP_RETVAL(gmpnum_result);
1288 mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
1289 } else {
1290 mpz_ptr gmpnum_base;
1291 FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
1292 INIT_GMP_RETVAL(gmpnum_result);
1293 mpz_pow_ui(gmpnum_result, gmpnum_base, exp);
1294 FREE_GMP_TEMP(temp_base);
1295 }
1296 }
1297 /* }}} */
1298
1299 /* {{{ Raise base to power exp and take result modulo mod */
ZEND_FUNCTION(gmp_powm)1300 ZEND_FUNCTION(gmp_powm)
1301 {
1302 zval *base_arg, *exp_arg, *mod_arg;
1303 mpz_ptr gmpnum_base, gmpnum_exp, gmpnum_mod, gmpnum_result;
1304 int use_ui = 0;
1305 gmp_temp_t temp_base, temp_exp, temp_mod;
1306
1307 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz", &base_arg, &exp_arg, &mod_arg) == FAILURE){
1308 RETURN_THROWS();
1309 }
1310
1311 FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
1312
1313 if (Z_TYPE_P(exp_arg) == IS_LONG && Z_LVAL_P(exp_arg) >= 0) {
1314 use_ui = 1;
1315 temp_exp.is_used = 0;
1316 } else {
1317 FETCH_GMP_ZVAL_DEP(gmpnum_exp, exp_arg, temp_exp, temp_base, 2);
1318 if (mpz_sgn(gmpnum_exp) < 0) {
1319 zend_argument_value_error(2, "must be greater than or equal to 0");
1320 FREE_GMP_TEMP(temp_base);
1321 FREE_GMP_TEMP(temp_exp);
1322 RETURN_THROWS();
1323 }
1324 }
1325 FETCH_GMP_ZVAL_DEP_DEP(gmpnum_mod, mod_arg, temp_mod, temp_exp, temp_base, 3);
1326
1327 if (!mpz_cmp_ui(gmpnum_mod, 0)) {
1328 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
1329 FREE_GMP_TEMP(temp_base);
1330 FREE_GMP_TEMP(temp_exp);
1331 FREE_GMP_TEMP(temp_mod);
1332 RETURN_THROWS();
1333 }
1334
1335 INIT_GMP_RETVAL(gmpnum_result);
1336 if (use_ui) {
1337 mpz_powm_ui(gmpnum_result, gmpnum_base, (zend_ulong) Z_LVAL_P(exp_arg), gmpnum_mod);
1338 } else {
1339 mpz_powm(gmpnum_result, gmpnum_base, gmpnum_exp, gmpnum_mod);
1340 FREE_GMP_TEMP(temp_exp);
1341 }
1342
1343 FREE_GMP_TEMP(temp_base);
1344 FREE_GMP_TEMP(temp_mod);
1345 }
1346 /* }}} */
1347
1348 /* {{{ Takes integer part of square root of a */
ZEND_FUNCTION(gmp_sqrt)1349 ZEND_FUNCTION(gmp_sqrt)
1350 {
1351 zval *a_arg;
1352 mpz_ptr gmpnum_a, gmpnum_result;
1353 gmp_temp_t temp_a;
1354
1355 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1356 RETURN_THROWS();
1357 }
1358
1359 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1360
1361 if (mpz_sgn(gmpnum_a) < 0) {
1362 zend_argument_value_error(1, "must be greater than or equal to 0");
1363 FREE_GMP_TEMP(temp_a);
1364 RETURN_THROWS();
1365 }
1366
1367 INIT_GMP_RETVAL(gmpnum_result);
1368 mpz_sqrt(gmpnum_result, gmpnum_a);
1369 FREE_GMP_TEMP(temp_a);
1370 }
1371 /* }}} */
1372
1373 /* {{{ Square root with remainder */
ZEND_FUNCTION(gmp_sqrtrem)1374 ZEND_FUNCTION(gmp_sqrtrem)
1375 {
1376 zval *a_arg;
1377 mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
1378 gmp_temp_t temp_a;
1379 zval result1, result2;
1380
1381 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1382 RETURN_THROWS();
1383 }
1384
1385 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1386
1387 if (mpz_sgn(gmpnum_a) < 0) {
1388 zend_argument_value_error(1, "must be greater than or equal to 0");
1389 FREE_GMP_TEMP(temp_a);
1390 RETURN_THROWS();
1391 }
1392
1393 gmp_create(&result1, &gmpnum_result1);
1394 gmp_create(&result2, &gmpnum_result2);
1395
1396 array_init(return_value);
1397 add_next_index_zval(return_value, &result1);
1398 add_next_index_zval(return_value, &result2);
1399
1400 mpz_sqrtrem(gmpnum_result1, gmpnum_result2, gmpnum_a);
1401 FREE_GMP_TEMP(temp_a);
1402 }
1403 /* }}} */
1404
1405 /* {{{ Takes integer part of nth root */
ZEND_FUNCTION(gmp_root)1406 ZEND_FUNCTION(gmp_root)
1407 {
1408 zval *a_arg;
1409 zend_long nth;
1410 mpz_ptr gmpnum_a, gmpnum_result;
1411 gmp_temp_t temp_a;
1412
1413 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
1414 RETURN_THROWS();
1415 }
1416
1417 if (nth <= 0) {
1418 zend_argument_value_error(2, "must be greater than 0");
1419 RETURN_THROWS();
1420 }
1421
1422 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1423
1424 if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
1425 zend_argument_value_error(2, "must be odd if argument #1 ($a) is negative");
1426 FREE_GMP_TEMP(temp_a);
1427 RETURN_THROWS();
1428 }
1429
1430 INIT_GMP_RETVAL(gmpnum_result);
1431 mpz_root(gmpnum_result, gmpnum_a, (gmp_ulong) nth);
1432 FREE_GMP_TEMP(temp_a);
1433 }
1434 /* }}} */
1435
1436 /* {{{ Calculates integer part of nth root and remainder */
ZEND_FUNCTION(gmp_rootrem)1437 ZEND_FUNCTION(gmp_rootrem)
1438 {
1439 zval *a_arg;
1440 zend_long nth;
1441 mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
1442 gmp_temp_t temp_a;
1443 zval result1, result2;
1444
1445 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
1446 RETURN_THROWS();
1447 }
1448
1449 if (nth <= 0) {
1450 zend_argument_value_error(2, "must be greater than or equal to 1");
1451 RETURN_THROWS();
1452 }
1453
1454 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1455
1456 if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
1457 zend_argument_value_error(2, "must be odd if argument #1 ($a) is negative");
1458 FREE_GMP_TEMP(temp_a);
1459 RETURN_THROWS();
1460 }
1461
1462 gmp_create(&result1, &gmpnum_result1);
1463 gmp_create(&result2, &gmpnum_result2);
1464
1465 array_init(return_value);
1466 add_next_index_zval(return_value, &result1);
1467 add_next_index_zval(return_value, &result2);
1468
1469 #if GMP_51_OR_NEWER
1470 /* mpz_rootrem() is supported since GMP 4.2, but buggy wrt odd roots
1471 * of negative numbers */
1472 mpz_rootrem(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) nth);
1473 #else
1474 mpz_root(gmpnum_result1, gmpnum_a, (gmp_ulong) nth);
1475 mpz_pow_ui(gmpnum_result2, gmpnum_result1, (gmp_ulong) nth);
1476 mpz_sub(gmpnum_result2, gmpnum_a, gmpnum_result2);
1477 #endif
1478
1479 FREE_GMP_TEMP(temp_a);
1480 }
1481 /* }}} */
1482
1483 /* {{{ Checks if a is an exact square */
ZEND_FUNCTION(gmp_perfect_square)1484 ZEND_FUNCTION(gmp_perfect_square)
1485 {
1486 zval *a_arg;
1487 mpz_ptr gmpnum_a;
1488 gmp_temp_t temp_a;
1489
1490 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1491 RETURN_THROWS();
1492 }
1493
1494 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1495
1496 RETVAL_BOOL((mpz_perfect_square_p(gmpnum_a) != 0));
1497 FREE_GMP_TEMP(temp_a);
1498 }
1499 /* }}} */
1500
1501 /* {{{ Checks if a is a perfect power */
ZEND_FUNCTION(gmp_perfect_power)1502 ZEND_FUNCTION(gmp_perfect_power)
1503 {
1504 zval *a_arg;
1505 mpz_ptr gmpnum_a;
1506 gmp_temp_t temp_a;
1507
1508 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1509 RETURN_THROWS();
1510 }
1511
1512 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1513
1514 RETVAL_BOOL((mpz_perfect_power_p(gmpnum_a) != 0));
1515 FREE_GMP_TEMP(temp_a);
1516 }
1517 /* }}} */
1518
1519 /* {{{ Checks if a is "probably prime" */
ZEND_FUNCTION(gmp_prob_prime)1520 ZEND_FUNCTION(gmp_prob_prime)
1521 {
1522 zval *gmpnumber_arg;
1523 mpz_ptr gmpnum_a;
1524 zend_long reps = 10;
1525 gmp_temp_t temp_a;
1526
1527 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &reps) == FAILURE) {
1528 RETURN_THROWS();
1529 }
1530
1531 FETCH_GMP_ZVAL(gmpnum_a, gmpnumber_arg, temp_a, 1);
1532
1533 RETVAL_LONG(mpz_probab_prime_p(gmpnum_a, (int)reps));
1534 FREE_GMP_TEMP(temp_a);
1535 }
1536 /* }}} */
1537
1538 /* {{{ Computes greatest common denominator (gcd) of a and b */
ZEND_FUNCTION(gmp_gcd)1539 ZEND_FUNCTION(gmp_gcd)
1540 {
1541 gmp_binary_ui_op(mpz_gcd, gmp_mpz_gcd_ui);
1542 }
1543 /* }}} */
1544
1545 /* {{{ Computes least common multiple (lcm) of a and b */
ZEND_FUNCTION(gmp_lcm)1546 ZEND_FUNCTION(gmp_lcm)
1547 {
1548 gmp_binary_ui_op(mpz_lcm, mpz_lcm_ui);
1549 }
1550 /* }}} */
1551
1552 /* {{{ Computes G, S, and T, such that AS + BT = G = `gcd' (A, B) */
ZEND_FUNCTION(gmp_gcdext)1553 ZEND_FUNCTION(gmp_gcdext)
1554 {
1555 zval *a_arg, *b_arg;
1556 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_t, gmpnum_s, gmpnum_g;
1557 gmp_temp_t temp_a, temp_b;
1558 zval result_g, result_s, result_t;
1559
1560 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1561 RETURN_THROWS();
1562 }
1563
1564 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1565 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1566
1567 gmp_create(&result_g, &gmpnum_g);
1568 gmp_create(&result_s, &gmpnum_s);
1569 gmp_create(&result_t, &gmpnum_t);
1570
1571 array_init(return_value);
1572 add_assoc_zval(return_value, "g", &result_g);
1573 add_assoc_zval(return_value, "s", &result_s);
1574 add_assoc_zval(return_value, "t", &result_t);
1575
1576 mpz_gcdext(gmpnum_g, gmpnum_s, gmpnum_t, gmpnum_a, gmpnum_b);
1577 FREE_GMP_TEMP(temp_a);
1578 FREE_GMP_TEMP(temp_b);
1579 }
1580 /* }}} */
1581
1582 /* {{{ Computes the inverse of a modulo b */
ZEND_FUNCTION(gmp_invert)1583 ZEND_FUNCTION(gmp_invert)
1584 {
1585 zval *a_arg, *b_arg;
1586 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
1587 gmp_temp_t temp_a, temp_b;
1588 int res;
1589
1590 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1591 RETURN_THROWS();
1592 }
1593
1594 if (Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) == 0) {
1595 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1596 RETURN_THROWS();
1597 }
1598
1599 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1600 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1601
1602 if (!mpz_cmp_ui(gmpnum_b, 0)) {
1603 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1604 FREE_GMP_TEMP(temp_a);
1605 FREE_GMP_TEMP(temp_b);
1606 RETURN_THROWS();
1607 }
1608
1609 INIT_GMP_RETVAL(gmpnum_result);
1610 res = mpz_invert(gmpnum_result, gmpnum_a, gmpnum_b);
1611 FREE_GMP_TEMP(temp_a);
1612 FREE_GMP_TEMP(temp_b);
1613 if (!res) {
1614 zval_ptr_dtor(return_value);
1615 RETURN_FALSE;
1616 }
1617 }
1618 /* }}} */
1619
1620 /* {{{ Computes Jacobi symbol */
ZEND_FUNCTION(gmp_jacobi)1621 ZEND_FUNCTION(gmp_jacobi)
1622 {
1623 zval *a_arg, *b_arg;
1624 mpz_ptr gmpnum_a, gmpnum_b;
1625 gmp_temp_t temp_a, temp_b;
1626
1627 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1628 RETURN_THROWS();
1629 }
1630
1631 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1632 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1633
1634 RETVAL_LONG(mpz_jacobi(gmpnum_a, gmpnum_b));
1635
1636 FREE_GMP_TEMP(temp_a);
1637 FREE_GMP_TEMP(temp_b);
1638 }
1639 /* }}} */
1640
1641 /* {{{ Computes Legendre symbol */
ZEND_FUNCTION(gmp_legendre)1642 ZEND_FUNCTION(gmp_legendre)
1643 {
1644 zval *a_arg, *b_arg;
1645 mpz_ptr gmpnum_a, gmpnum_b;
1646 gmp_temp_t temp_a, temp_b;
1647
1648 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1649 RETURN_THROWS();
1650 }
1651
1652 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1653 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1654
1655 RETVAL_LONG(mpz_legendre(gmpnum_a, gmpnum_b));
1656
1657 FREE_GMP_TEMP(temp_a);
1658 FREE_GMP_TEMP(temp_b);
1659 }
1660 /* }}} */
1661
1662 /* {{{ Computes the Kronecker symbol */
ZEND_FUNCTION(gmp_kronecker)1663 ZEND_FUNCTION(gmp_kronecker)
1664 {
1665 zval *a_arg, *b_arg;
1666 mpz_ptr gmpnum_a, gmpnum_b;
1667 gmp_temp_t temp_a, temp_b;
1668 bool use_a_si = 0, use_b_si = 0;
1669 int result;
1670
1671 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1672 RETURN_THROWS();
1673 }
1674
1675 if (Z_TYPE_P(a_arg) == IS_LONG && Z_TYPE_P(b_arg) != IS_LONG) {
1676 use_a_si = 1;
1677 temp_a.is_used = 0;
1678 } else {
1679 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1680 }
1681
1682 if (Z_TYPE_P(b_arg) == IS_LONG) {
1683 use_b_si = 1;
1684 temp_b.is_used = 0;
1685 } else {
1686 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1687 }
1688
1689 if (use_a_si) {
1690 ZEND_ASSERT(use_b_si == 0);
1691 result = mpz_si_kronecker((gmp_long) Z_LVAL_P(a_arg), gmpnum_b);
1692 } else if (use_b_si) {
1693 result = mpz_kronecker_si(gmpnum_a, (gmp_long) Z_LVAL_P(b_arg));
1694 } else {
1695 result = mpz_kronecker(gmpnum_a, gmpnum_b);
1696 }
1697
1698 FREE_GMP_TEMP(temp_a);
1699 FREE_GMP_TEMP(temp_b);
1700
1701 RETURN_LONG(result);
1702 }
1703 /* }}} */
1704
1705 /* {{{ Compares two numbers */
ZEND_FUNCTION(gmp_cmp)1706 ZEND_FUNCTION(gmp_cmp)
1707 {
1708 zval *a_arg, *b_arg;
1709
1710 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1711 RETURN_THROWS();
1712 }
1713
1714 gmp_cmp(return_value, a_arg, b_arg, /* is_operator */ false);
1715 }
1716 /* }}} */
1717
1718 /* {{{ Gets the sign of the number */
ZEND_FUNCTION(gmp_sign)1719 ZEND_FUNCTION(gmp_sign)
1720 {
1721 /* Can't use gmp_unary_opl here, because mpz_sgn is a macro */
1722 zval *a_arg;
1723 mpz_ptr gmpnum_a;
1724 gmp_temp_t temp_a;
1725
1726 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1727 RETURN_THROWS();
1728 }
1729
1730 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1731
1732 RETVAL_LONG(mpz_sgn(gmpnum_a));
1733 FREE_GMP_TEMP(temp_a);
1734 }
1735 /* }}} */
1736
gmp_init_random(void)1737 static void gmp_init_random(void)
1738 {
1739 if (!GMPG(rand_initialized)) {
1740 /* Initialize */
1741 gmp_randinit_mt(GMPG(rand_state));
1742 /* Seed */
1743 unsigned long int seed = 0;
1744 if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
1745 seed = (unsigned long int)php_random_generate_fallback_seed();
1746 }
1747 gmp_randseed_ui(GMPG(rand_state), seed);
1748
1749 GMPG(rand_initialized) = 1;
1750 }
1751 }
1752
1753 /* {{{ Seed the RNG */
ZEND_FUNCTION(gmp_random_seed)1754 ZEND_FUNCTION(gmp_random_seed)
1755 {
1756 zval *seed;
1757 gmp_temp_t temp_a;
1758
1759 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &seed) == FAILURE) {
1760 RETURN_THROWS();
1761 }
1762
1763 gmp_init_random();
1764
1765 if (Z_TYPE_P(seed) == IS_LONG && Z_LVAL_P(seed) >= 0) {
1766 gmp_randseed_ui(GMPG(rand_state), Z_LVAL_P(seed));
1767 }
1768 else {
1769 mpz_ptr gmpnum_seed;
1770
1771 FETCH_GMP_ZVAL(gmpnum_seed, seed, temp_a, 1);
1772
1773 gmp_randseed(GMPG(rand_state), gmpnum_seed);
1774
1775 FREE_GMP_TEMP(temp_a);
1776 }
1777 }
1778 /* }}} */
1779
1780 /* {{{ Gets a random number in the range 0 to (2 ** n) - 1 */
ZEND_FUNCTION(gmp_random_bits)1781 ZEND_FUNCTION(gmp_random_bits)
1782 {
1783 zend_long bits;
1784 mpz_ptr gmpnum_result;
1785
1786 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &bits) == FAILURE) {
1787 RETURN_THROWS();
1788 }
1789
1790 if (bits <= 0) {
1791 zend_argument_value_error(1, "must be greater than or equal to 1");
1792 RETURN_THROWS();
1793 }
1794
1795 INIT_GMP_RETVAL(gmpnum_result);
1796 gmp_init_random();
1797
1798 mpz_urandomb(gmpnum_result, GMPG(rand_state), bits);
1799 }
1800 /* }}} */
1801
1802 /* {{{ Gets a random number in the range min to max */
ZEND_FUNCTION(gmp_random_range)1803 ZEND_FUNCTION(gmp_random_range)
1804 {
1805 zval *min_arg, *max_arg;
1806 mpz_ptr gmpnum_max, gmpnum_result;
1807 mpz_t gmpnum_range;
1808 gmp_temp_t temp_a, temp_b;
1809
1810 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &min_arg, &max_arg) == FAILURE) {
1811 RETURN_THROWS();
1812 }
1813
1814 gmp_init_random();
1815
1816 FETCH_GMP_ZVAL(gmpnum_max, max_arg, temp_a, 2);
1817
1818 if (Z_TYPE_P(min_arg) == IS_LONG && Z_LVAL_P(min_arg) >= 0) {
1819 if (mpz_cmp_ui(gmpnum_max, Z_LVAL_P(min_arg)) <= 0) {
1820 FREE_GMP_TEMP(temp_a);
1821 zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
1822 RETURN_THROWS();
1823 }
1824
1825 INIT_GMP_RETVAL(gmpnum_result);
1826 mpz_init(gmpnum_range);
1827
1828 if (Z_LVAL_P(min_arg) != 0) {
1829 mpz_sub_ui(gmpnum_range, gmpnum_max, Z_LVAL_P(min_arg) - 1);
1830 } else {
1831 mpz_add_ui(gmpnum_range, gmpnum_max, 1);
1832 }
1833
1834 mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1835
1836 if (Z_LVAL_P(min_arg) != 0) {
1837 mpz_add_ui(gmpnum_result, gmpnum_result, Z_LVAL_P(min_arg));
1838 }
1839
1840 mpz_clear(gmpnum_range);
1841 FREE_GMP_TEMP(temp_a);
1842 } else {
1843 mpz_ptr gmpnum_min;
1844
1845 FETCH_GMP_ZVAL_DEP(gmpnum_min, min_arg, temp_b, temp_a, 1);
1846
1847 if (mpz_cmp(gmpnum_max, gmpnum_min) <= 0) {
1848 FREE_GMP_TEMP(temp_b);
1849 FREE_GMP_TEMP(temp_a);
1850 zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
1851 RETURN_THROWS();
1852 }
1853
1854 INIT_GMP_RETVAL(gmpnum_result);
1855 mpz_init(gmpnum_range);
1856
1857 mpz_sub(gmpnum_range, gmpnum_max, gmpnum_min);
1858 mpz_add_ui(gmpnum_range, gmpnum_range, 1);
1859 mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1860 mpz_add(gmpnum_result, gmpnum_result, gmpnum_min);
1861
1862 mpz_clear(gmpnum_range);
1863 FREE_GMP_TEMP(temp_b);
1864 FREE_GMP_TEMP(temp_a);
1865 }
1866 }
1867 /* }}} */
1868
1869 /* {{{ Calculates logical AND of a and b */
ZEND_FUNCTION(gmp_and)1870 ZEND_FUNCTION(gmp_and)
1871 {
1872 gmp_binary_op(mpz_and);
1873 }
1874 /* }}} */
1875
1876 /* {{{ Calculates logical OR of a and b */
ZEND_FUNCTION(gmp_or)1877 ZEND_FUNCTION(gmp_or)
1878 {
1879 gmp_binary_op(mpz_ior);
1880 }
1881 /* }}} */
1882
1883 /* {{{ Calculates one's complement of a */
ZEND_FUNCTION(gmp_com)1884 ZEND_FUNCTION(gmp_com)
1885 {
1886 gmp_unary_op(mpz_com);
1887 }
1888 /* }}} */
1889
1890 /* {{{ Finds next prime of a */
ZEND_FUNCTION(gmp_nextprime)1891 ZEND_FUNCTION(gmp_nextprime)
1892 {
1893 gmp_unary_op(mpz_nextprime);
1894 }
1895 /* }}} */
1896
1897 /* {{{ Calculates logical exclusive OR of a and b */
ZEND_FUNCTION(gmp_xor)1898 ZEND_FUNCTION(gmp_xor)
1899 {
1900 gmp_binary_op(mpz_xor);
1901 }
1902 /* }}} */
1903
1904 /* {{{ Sets or clear bit in a */
ZEND_FUNCTION(gmp_setbit)1905 ZEND_FUNCTION(gmp_setbit)
1906 {
1907 zval *a_arg;
1908 zend_long index;
1909 bool set = 1;
1910 mpz_ptr gmpnum_a;
1911
1912 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|b", &a_arg, gmp_ce, &index, &set) == FAILURE) {
1913 RETURN_THROWS();
1914 }
1915
1916 if (index < 0) {
1917 zend_argument_value_error(2, "must be greater than or equal to 0");
1918 RETURN_THROWS();
1919 }
1920 if (index / GMP_NUMB_BITS >= INT_MAX) {
1921 zend_argument_value_error(2, "must be less than %d * %d", INT_MAX, GMP_NUMB_BITS);
1922 RETURN_THROWS();
1923 }
1924
1925 gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
1926
1927 if (set) {
1928 mpz_setbit(gmpnum_a, index);
1929 } else {
1930 mpz_clrbit(gmpnum_a, index);
1931 }
1932 }
1933 /* }}} */
1934
1935 /* {{{ Clears bit in a */
ZEND_FUNCTION(gmp_clrbit)1936 ZEND_FUNCTION(gmp_clrbit)
1937 {
1938 zval *a_arg;
1939 zend_long index;
1940 mpz_ptr gmpnum_a;
1941
1942 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &a_arg, gmp_ce, &index) == FAILURE){
1943 RETURN_THROWS();
1944 }
1945
1946 if (index < 0) {
1947 zend_argument_value_error(2, "must be greater than or equal to 0");
1948 RETURN_THROWS();
1949 }
1950
1951 gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
1952 mpz_clrbit(gmpnum_a, index);
1953 }
1954 /* }}} */
1955
1956 /* {{{ Tests if bit is set in a */
ZEND_FUNCTION(gmp_testbit)1957 ZEND_FUNCTION(gmp_testbit)
1958 {
1959 zval *a_arg;
1960 zend_long index;
1961 mpz_ptr gmpnum_a;
1962 gmp_temp_t temp_a;
1963
1964 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &index) == FAILURE){
1965 RETURN_THROWS();
1966 }
1967
1968 if (index < 0) {
1969 zend_argument_value_error(2, "must be greater than or equal to 0");
1970 RETURN_THROWS();
1971 }
1972
1973 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1974 RETVAL_BOOL(mpz_tstbit(gmpnum_a, index));
1975 FREE_GMP_TEMP(temp_a);
1976 }
1977 /* }}} */
1978
1979 /* {{{ Calculates the population count of a */
ZEND_FUNCTION(gmp_popcount)1980 ZEND_FUNCTION(gmp_popcount)
1981 {
1982 gmp_unary_opl(mpz_popcount);
1983 }
1984 /* }}} */
1985
1986 /* {{{ Calculates hamming distance between a and b */
ZEND_FUNCTION(gmp_hamdist)1987 ZEND_FUNCTION(gmp_hamdist)
1988 {
1989 zval *a_arg, *b_arg;
1990 mpz_ptr gmpnum_a, gmpnum_b;
1991 gmp_temp_t temp_a, temp_b;
1992
1993 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1994 RETURN_THROWS();
1995 }
1996
1997 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1998 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1999
2000 RETVAL_LONG(mpz_hamdist(gmpnum_a, gmpnum_b));
2001
2002 FREE_GMP_TEMP(temp_a);
2003 FREE_GMP_TEMP(temp_b);
2004 }
2005 /* }}} */
2006
2007 /* {{{ Finds first zero bit */
ZEND_FUNCTION(gmp_scan0)2008 ZEND_FUNCTION(gmp_scan0)
2009 {
2010 zval *a_arg;
2011 mpz_ptr gmpnum_a;
2012 gmp_temp_t temp_a;
2013 zend_long start;
2014
2015 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2016 RETURN_THROWS();
2017 }
2018
2019 if (start < 0) {
2020 zend_argument_value_error(2, "must be greater than or equal to 0");
2021 RETURN_THROWS();
2022 }
2023
2024 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2025
2026 RETVAL_LONG(mpz_scan0(gmpnum_a, start));
2027 FREE_GMP_TEMP(temp_a);
2028 }
2029 /* }}} */
2030
2031 /* {{{ Finds first non-zero bit */
ZEND_FUNCTION(gmp_scan1)2032 ZEND_FUNCTION(gmp_scan1)
2033 {
2034 zval *a_arg;
2035 mpz_ptr gmpnum_a;
2036 gmp_temp_t temp_a;
2037 zend_long start;
2038
2039 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2040 RETURN_THROWS();
2041 }
2042
2043 if (start < 0) {
2044 zend_argument_value_error(2, "must be greater than or equal to 0");
2045 RETURN_THROWS();
2046 }
2047
2048 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2049
2050 RETVAL_LONG(mpz_scan1(gmpnum_a, start));
2051 FREE_GMP_TEMP(temp_a);
2052 }
2053 /* }}} */
2054
ZEND_METHOD(GMP,__construct)2055 ZEND_METHOD(GMP, __construct)
2056 {
2057 zend_string *arg_str = NULL;
2058 zend_long arg_l = 0;
2059 zend_long base = 0;
2060
2061 ZEND_PARSE_PARAMETERS_START(0, 2)
2062 Z_PARAM_OPTIONAL
2063 Z_PARAM_STR_OR_LONG(arg_str, arg_l)
2064 Z_PARAM_LONG(base)
2065 ZEND_PARSE_PARAMETERS_END();
2066
2067 if (!gmp_verify_base(base, 2)) {
2068 RETURN_THROWS();
2069 }
2070
2071 return_value = ZEND_THIS;
2072 mpz_ptr gmp_number = GET_GMP_FROM_ZVAL(ZEND_THIS);
2073
2074 if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
2075 RETURN_THROWS();
2076 }
2077 }
2078
ZEND_METHOD(GMP,__serialize)2079 ZEND_METHOD(GMP, __serialize)
2080 {
2081 ZEND_PARSE_PARAMETERS_NONE();
2082
2083 zval zv;
2084 array_init(return_value);
2085
2086 mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(ZEND_THIS);
2087 gmp_strval(&zv, gmpnum, 16);
2088 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
2089
2090 HashTable *props = Z_OBJ_P(ZEND_THIS)->properties;
2091 if (props && zend_hash_num_elements(props) != 0) {
2092 ZVAL_ARR(&zv, zend_proptable_to_symtable(
2093 zend_std_get_properties(Z_OBJ_P(ZEND_THIS)), /* always duplicate */ 1));
2094 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
2095 }
2096 }
2097
ZEND_METHOD(GMP,__unserialize)2098 ZEND_METHOD(GMP, __unserialize)
2099 {
2100 HashTable *data;
2101
2102 ZEND_PARSE_PARAMETERS_START(1, 1)
2103 Z_PARAM_ARRAY_HT(data)
2104 ZEND_PARSE_PARAMETERS_END();
2105
2106 zval *num = zend_hash_index_find(data, 0);
2107 if (!num || Z_TYPE_P(num) != IS_STRING ||
2108 convert_to_gmp(GET_GMP_FROM_ZVAL(ZEND_THIS), num, 16, 0) == FAILURE) {
2109 zend_throw_exception(NULL, "Could not unserialize number", 0);
2110 RETURN_THROWS();
2111 }
2112
2113 zval *props = zend_hash_index_find(data, 1);
2114 if (props) {
2115 if (Z_TYPE_P(props) != IS_ARRAY) {
2116 zend_throw_exception(NULL, "Could not unserialize properties", 0);
2117 RETURN_THROWS();
2118 }
2119
2120 object_properties_load(Z_OBJ_P(ZEND_THIS), Z_ARRVAL_P(props));
2121 }
2122 }
2123