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