root/ext/gmp/gmp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ZEND_TSRMLS_CACHE_DEFINE
  2. gmp_free_object_storage
  3. gmp_create_object_ex
  4. gmp_create_object
  5. gmp_create
  6. gmp_cast_object
  7. gmp_get_debug_info
  8. gmp_clone_obj
  9. shift_operator_helper
  10. gmp_do_operation_ex
  11. gmp_do_operation
  12. gmp_compare
  13. gmp_serialize
  14. gmp_unserialize
  15. ZEND_GINIT_FUNCTION
  16. ZEND_MINIT_FUNCTION
  17. ZEND_MODULE_DEACTIVATE_D
  18. ZEND_MODULE_INFO_D
  19. convert_to_gmp
  20. gmp_strval
  21. gmp_cmp
  22. gmp_zval_binary_ui_op
  23. gmp_zval_binary_ui_op2
  24. _gmp_binary_ui_op
  25. gmp_zval_unary_op
  26. gmp_zval_unary_ui_op
  27. _gmp_unary_ui_op
  28. _gmp_unary_op
  29. _gmp_unary_opl
  30. _gmp_binary_opl
  31. ZEND_FUNCTION
  32. gmp_import_export_validate
  33. ZEND_FUNCTION
  34. ZEND_FUNCTION
  35. ZEND_FUNCTION
  36. ZEND_FUNCTION
  37. ZEND_FUNCTION
  38. ZEND_FUNCTION
  39. ZEND_FUNCTION
  40. ZEND_FUNCTION
  41. ZEND_FUNCTION
  42. ZEND_FUNCTION
  43. ZEND_FUNCTION
  44. ZEND_FUNCTION
  45. ZEND_FUNCTION
  46. ZEND_FUNCTION
  47. ZEND_FUNCTION
  48. ZEND_FUNCTION
  49. ZEND_FUNCTION
  50. ZEND_FUNCTION
  51. ZEND_FUNCTION
  52. ZEND_FUNCTION
  53. ZEND_FUNCTION
  54. ZEND_FUNCTION
  55. ZEND_FUNCTION
  56. ZEND_FUNCTION
  57. ZEND_FUNCTION
  58. ZEND_FUNCTION
  59. ZEND_FUNCTION
  60. ZEND_FUNCTION
  61. ZEND_FUNCTION
  62. ZEND_FUNCTION
  63. gmp_init_random
  64. ZEND_FUNCTION
  65. ZEND_FUNCTION
  66. ZEND_FUNCTION
  67. ZEND_FUNCTION
  68. ZEND_FUNCTION
  69. ZEND_FUNCTION
  70. ZEND_FUNCTION
  71. ZEND_FUNCTION
  72. ZEND_FUNCTION
  73. ZEND_FUNCTION
  74. ZEND_FUNCTION
  75. ZEND_FUNCTION
  76. ZEND_FUNCTION
  77. ZEND_FUNCTION
  78. ZEND_FUNCTION
  79. ZEND_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */