root/ext/gmp/gmp.c

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

DEFINITIONS

This source file includes following definitions.
  1. ZEND_GET_MODULE
  2. gmp_get_long
  3. gmp_free_object_storage
  4. gmp_create_object_ex
  5. gmp_create_object
  6. gmp_create_ex
  7. gmp_create
  8. gmp_cast_object
  9. gmp_get_debug_info
  10. gmp_clone_obj
  11. shift_operator_helper
  12. gmp_do_operation_ex
  13. gmp_do_operation
  14. gmp_compare
  15. gmp_serialize
  16. gmp_unserialize
  17. ZEND_GINIT_FUNCTION
  18. ZEND_MINIT_FUNCTION
  19. ZEND_MODULE_DEACTIVATE_D
  20. ZEND_MODULE_INFO_D
  21. convert_to_gmp
  22. gmp_strval
  23. gmp_cmp
  24. gmp_zval_binary_ui_op
  25. gmp_zval_binary_ui_op2
  26. _gmp_binary_ui_op
  27. gmp_zval_unary_op
  28. gmp_zval_unary_ui_op
  29. _gmp_unary_ui_op
  30. _gmp_unary_op
  31. _gmp_unary_opl
  32. _gmp_binary_opl
  33. ZEND_FUNCTION
  34. gmp_import_export_validate
  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. ZEND_FUNCTION
  64. ZEND_FUNCTION
  65. gmp_init_random
  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
  80. ZEND_FUNCTION

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

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