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