2000-11-27 02:36:16 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
2019-01-30 17:00:23 +08:00
|
|
|
| Copyright (c) The PHP Group |
|
2000-11-27 02:36:16 +08:00
|
|
|
+----------------------------------------------------------------------+
|
2006-01-01 20:51:34 +08:00
|
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
2000-11-27 02:36:16 +08:00
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
2003-06-11 04:04:29 +08:00
|
|
|
| available through the world-wide-web at the following url: |
|
2021-05-06 18:16:35 +08:00
|
|
|
| https://www.php.net/license/3_01.txt |
|
2000-11-27 02:36:16 +08:00
|
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
|
|
+----------------------------------------------------------------------+
|
2002-02-28 16:29:35 +08:00
|
|
|
| Author: Stanislav Malyshev <stas@php.net> |
|
2000-11-27 02:36:16 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
2001-05-24 18:07:29 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2000-11-27 02:36:16 +08:00
|
|
|
#include "php.h"
|
|
|
|
#include "php_ini.h"
|
|
|
|
#include "php_gmp.h"
|
2017-10-03 05:26:05 +08:00
|
|
|
#include "php_gmp_int.h"
|
2000-11-27 02:36:16 +08:00
|
|
|
#include "ext/standard/info.h"
|
2013-10-30 03:58:30 +08:00
|
|
|
#include "ext/standard/php_var.h"
|
2014-09-21 04:42:02 +08:00
|
|
|
#include "zend_smart_str_public.h"
|
2013-06-17 23:49:24 +08:00
|
|
|
#include "zend_exceptions.h"
|
2000-11-27 02:36:16 +08:00
|
|
|
|
|
|
|
#include <gmp.h>
|
2003-11-19 12:44:06 +08:00
|
|
|
|
|
|
|
/* Needed for gmp_random() */
|
2022-07-19 11:10:55 +08:00
|
|
|
#include "ext/random/php_random.h"
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2022-06-06 20:38:03 +08:00
|
|
|
#define GMP_ROUND_ZERO 0
|
|
|
|
#define GMP_ROUND_PLUSINF 1
|
|
|
|
#define GMP_ROUND_MINUSINF 2
|
|
|
|
|
|
|
|
#ifdef mpir_version
|
|
|
|
#define GMP_MPIR_VERSION_STRING ((char *) mpir_version)
|
|
|
|
#endif
|
|
|
|
#define GMP_VERSION_STRING ((char *) gmp_version)
|
|
|
|
|
|
|
|
#define GMP_MSW_FIRST (1 << 0)
|
|
|
|
#define GMP_LSW_FIRST (1 << 1)
|
|
|
|
#define GMP_LITTLE_ENDIAN (1 << 2)
|
|
|
|
#define GMP_BIG_ENDIAN (1 << 3)
|
|
|
|
#define GMP_NATIVE_ENDIAN (1 << 4)
|
|
|
|
|
|
|
|
#include "gmp_arginfo.h"
|
|
|
|
|
2006-06-16 02:33:09 +08:00
|
|
|
ZEND_DECLARE_MODULE_GLOBALS(gmp)
|
|
|
|
static ZEND_GINIT_FUNCTION(gmp);
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ gmp_module_entry */
|
2000-11-27 02:36:16 +08:00
|
|
|
zend_module_entry gmp_module_entry = {
|
Fix for bugs #10133 and #15454.
Bug #15454 results from a bug in GMP. If you pass in a string '0xABCD' and
specify a base of 0, GMP figures out that it is hex and skips over the 0x
characters. If you specify base 16, then it doesn't skip those chars.
This was confirmed with the following test program:
#include <stdio.h>
#include <gmp.h>
int main()
{
char *str_one, *str_two;
mpz_t num_one, num_two;
mpz_init_set_str (num_one, "0x45", 0);
str_one = mpz_get_str(NULL, 10, num_one);
mpz_init_set_str (num_two, "0x45", 16);
str_two = mpz_get_str(NULL, 10, num_two);
printf("%s / %s\n", str_one, str_two);
mpz_clear (num_one);
mpz_clear (num_two);
return 0;
}
We now take anything that starts with 0[xX] as hexidecimal and anything
that starts 0[bB] as binary (this is what GMP does internally). We also
no longer force the base to 10 or 16, but instead let GMP decide what the
best base is, be it hex, dec, or octal.
2002-02-11 07:12:57 +08:00
|
|
|
STANDARD_MODULE_HEADER,
|
2000-11-27 02:36:16 +08:00
|
|
|
"gmp",
|
2020-04-05 05:51:10 +08:00
|
|
|
ext_functions,
|
2001-08-11 10:46:57 +08:00
|
|
|
ZEND_MODULE_STARTUP_N(gmp),
|
2007-01-12 20:32:15 +08:00
|
|
|
NULL,
|
Fix for bugs #10133 and #15454.
Bug #15454 results from a bug in GMP. If you pass in a string '0xABCD' and
specify a base of 0, GMP figures out that it is hex and skips over the 0x
characters. If you specify base 16, then it doesn't skip those chars.
This was confirmed with the following test program:
#include <stdio.h>
#include <gmp.h>
int main()
{
char *str_one, *str_two;
mpz_t num_one, num_two;
mpz_init_set_str (num_one, "0x45", 0);
str_one = mpz_get_str(NULL, 10, num_one);
mpz_init_set_str (num_two, "0x45", 16);
str_two = mpz_get_str(NULL, 10, num_two);
printf("%s / %s\n", str_one, str_two);
mpz_clear (num_one);
mpz_clear (num_two);
return 0;
}
We now take anything that starts with 0[xX] as hexidecimal and anything
that starts 0[bB] as binary (this is what GMP does internally). We also
no longer force the base to 10 or 16, but instead let GMP decide what the
best base is, be it hex, dec, or octal.
2002-02-11 07:12:57 +08:00
|
|
|
NULL,
|
2003-11-19 12:44:06 +08:00
|
|
|
ZEND_MODULE_DEACTIVATE_N(gmp),
|
2001-08-11 10:46:57 +08:00
|
|
|
ZEND_MODULE_INFO_N(gmp),
|
2015-03-24 03:33:54 +08:00
|
|
|
PHP_GMP_VERSION,
|
2006-06-16 02:33:09 +08:00
|
|
|
ZEND_MODULE_GLOBALS(gmp),
|
|
|
|
ZEND_GINIT(gmp),
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
STANDARD_MODULE_PROPERTIES_EX
|
2000-11-27 02:36:16 +08:00
|
|
|
};
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
|
|
|
#ifdef COMPILE_DL_GMP
|
2014-10-17 21:51:21 +08:00
|
|
|
#ifdef ZTS
|
2016-03-03 23:46:04 +08:00
|
|
|
ZEND_TSRMLS_CACHE_DEFINE()
|
2014-10-17 21:51:21 +08:00
|
|
|
#endif
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_GET_MODULE(gmp)
|
|
|
|
#endif
|
|
|
|
|
2017-10-03 05:26:05 +08:00
|
|
|
static zend_class_entry *gmp_ce;
|
2013-06-17 23:49:24 +08:00
|
|
|
static zend_object_handlers gmp_object_handlers;
|
|
|
|
|
2020-06-06 20:13:38 +08:00
|
|
|
PHP_GMP_API zend_class_entry *php_gmp_class_entry(void) {
|
2017-10-03 05:26:05 +08:00
|
|
|
return gmp_ce;
|
|
|
|
}
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
typedef struct _gmp_temp {
|
|
|
|
mpz_t num;
|
2021-01-15 19:30:54 +08:00
|
|
|
bool is_used;
|
2013-06-17 23:49:24 +08:00
|
|
|
} gmp_temp_t;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-09-29 02:39:19 +08:00
|
|
|
#define GMP_MAX_BASE 62
|
2010-09-15 18:51:55 +08:00
|
|
|
|
2015-04-16 22:27:42 +08:00
|
|
|
#define GMP_51_OR_NEWER \
|
|
|
|
((__GNU_MP_VERSION >= 6) || (__GNU_MP_VERSION >= 5 && __GNU_MP_VERSION_MINOR >= 1))
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
#define IS_GMP(zval) \
|
2014-12-14 06:06:14 +08:00
|
|
|
(Z_TYPE_P(zval) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zval), gmp_ce))
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2014-05-07 00:44:28 +08:00
|
|
|
#define GET_GMP_OBJECT_FROM_OBJ(obj) \
|
2017-10-03 05:26:05 +08:00
|
|
|
php_gmp_object_from_zend_object(obj)
|
2014-05-07 00:44:28 +08:00
|
|
|
#define GET_GMP_OBJECT_FROM_ZVAL(zv) \
|
|
|
|
GET_GMP_OBJECT_FROM_OBJ(Z_OBJ_P(zv))
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
#define GET_GMP_FROM_ZVAL(zval) \
|
2014-05-07 00:44:28 +08:00
|
|
|
GET_GMP_OBJECT_FROM_OBJ(Z_OBJ_P(zval))->num
|
2013-06-17 23:49:24 +08:00
|
|
|
|
|
|
|
/* The FETCH_GMP_ZVAL_* family of macros is used to fetch a gmp number
|
|
|
|
* (mpz_ptr) from a zval. If the zval is not a GMP instance, then we
|
|
|
|
* try to convert the value to a temporary gmp number using convert_to_gmp.
|
|
|
|
* This temporary number is stored in the temp argument, which is of type
|
|
|
|
* gmp_temp_t. This temporary value needs to be freed lateron using the
|
|
|
|
* FREE_GMP_TEMP macro.
|
|
|
|
*
|
2020-08-08 00:15:25 +08:00
|
|
|
* If the conversion to a gmp number fails, the macros RETURN_THROWS() due to TypeError.
|
2013-06-17 23:49:24 +08:00
|
|
|
* The _DEP / _DEP_DEP variants additionally free the temporary values
|
|
|
|
* passed in the last / last two arguments.
|
|
|
|
*
|
|
|
|
* If one zval can sometimes be fetched as a long you have to set the
|
|
|
|
* is_used member of the corresponding gmp_temp_t value to 0, otherwise
|
|
|
|
* the FREE_GMP_TEMP and *_DEP macros will not work properly.
|
|
|
|
*
|
|
|
|
* The three FETCH_GMP_ZVAL_* macros below are mostly copy & paste code
|
|
|
|
* as I couldn't find a way to combine them.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define FREE_GMP_TEMP(temp) \
|
|
|
|
if (temp.is_used) { \
|
|
|
|
mpz_clear(temp.num); \
|
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
#define FETCH_GMP_ZVAL_DEP_DEP(gmpnumber, zval, temp, dep1, dep2, arg_pos) \
|
2013-06-17 23:49:24 +08:00
|
|
|
if (IS_GMP(zval)) { \
|
|
|
|
gmpnumber = GET_GMP_FROM_ZVAL(zval); \
|
|
|
|
temp.is_used = 0; \
|
|
|
|
} else { \
|
|
|
|
mpz_init(temp.num); \
|
2020-08-08 00:15:25 +08:00
|
|
|
if (convert_to_gmp(temp.num, zval, 0, arg_pos) == FAILURE) { \
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_clear(temp.num); \
|
|
|
|
FREE_GMP_TEMP(dep1); \
|
|
|
|
FREE_GMP_TEMP(dep2); \
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS(); \
|
2013-06-17 23:49:24 +08:00
|
|
|
} \
|
|
|
|
temp.is_used = 1; \
|
|
|
|
gmpnumber = temp.num; \
|
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
#define FETCH_GMP_ZVAL_DEP(gmpnumber, zval, temp, dep, arg_pos) \
|
2013-06-17 23:49:24 +08:00
|
|
|
if (IS_GMP(zval)) { \
|
|
|
|
gmpnumber = GET_GMP_FROM_ZVAL(zval); \
|
|
|
|
temp.is_used = 0; \
|
|
|
|
} else { \
|
|
|
|
mpz_init(temp.num); \
|
2020-08-08 00:15:25 +08:00
|
|
|
if (convert_to_gmp(temp.num, zval, 0, arg_pos) == FAILURE) { \
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_clear(temp.num); \
|
|
|
|
FREE_GMP_TEMP(dep); \
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS(); \
|
2013-06-17 23:49:24 +08:00
|
|
|
} \
|
|
|
|
temp.is_used = 1; \
|
|
|
|
gmpnumber = temp.num; \
|
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
#define FETCH_GMP_ZVAL(gmpnumber, zval, temp, arg_pos) \
|
2013-06-17 23:49:24 +08:00
|
|
|
if (IS_GMP(zval)) { \
|
|
|
|
gmpnumber = GET_GMP_FROM_ZVAL(zval); \
|
|
|
|
temp.is_used = 0; \
|
|
|
|
} else { \
|
|
|
|
mpz_init(temp.num); \
|
2020-08-08 00:15:25 +08:00
|
|
|
if (convert_to_gmp(temp.num, zval, 0, arg_pos) == FAILURE) { \
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_clear(temp.num); \
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS(); \
|
2013-06-17 23:49:24 +08:00
|
|
|
} \
|
|
|
|
temp.is_used = 1; \
|
|
|
|
gmpnumber = temp.num; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define INIT_GMP_RETVAL(gmpnumber) \
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_create(return_value, &gmpnumber)
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2015-02-05 01:12:56 +08:00
|
|
|
static void gmp_strval(zval *result, mpz_t gmpnum, int base);
|
2020-12-29 09:31:15 +08:00
|
|
|
static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, uint32_t arg_pos);
|
2021-06-10 20:45:24 +08:00
|
|
|
static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operator);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2014-08-28 04:45:05 +08:00
|
|
|
/*
|
2013-06-17 23:49:24 +08:00
|
|
|
* The gmp_*_op functions provide an implementation for several common types
|
|
|
|
* of GMP functions. The gmp_zval_(unary|binary)_*_op functions have to be manually
|
|
|
|
* passed zvals to work on, whereas the gmp_(unary|binary)_*_op macros already
|
|
|
|
* include parameter parsing.
|
|
|
|
*/
|
|
|
|
typedef void (*gmp_unary_op_t)(mpz_ptr, mpz_srcptr);
|
2020-04-15 16:28:48 +08:00
|
|
|
typedef mp_bitcnt_t (*gmp_unary_opl_t)(mpz_srcptr);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2014-09-03 00:19:51 +08:00
|
|
|
typedef void (*gmp_unary_ui_op_t)(mpz_ptr, gmp_ulong);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
|
|
|
typedef void (*gmp_binary_op_t)(mpz_ptr, mpz_srcptr, mpz_srcptr);
|
|
|
|
|
2014-09-03 00:19:51 +08:00
|
|
|
typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong);
|
2013-06-17 23:49:24 +08:00
|
|
|
typedef void (*gmp_binary_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr);
|
2020-04-15 16:28:48 +08:00
|
|
|
typedef gmp_ulong (*gmp_binary_ui_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, gmp_ulong);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2021-06-10 20:45:24 +08:00
|
|
|
static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator);
|
2014-12-14 06:06:14 +08:00
|
|
|
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);
|
|
|
|
static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2020-04-15 16:28:48 +08:00
|
|
|
static void gmp_mpz_tdiv_q_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
|
|
|
|
mpz_tdiv_q_ui(a, b, c);
|
|
|
|
}
|
|
|
|
static void gmp_mpz_tdiv_r_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
|
|
|
|
mpz_tdiv_r_ui(a, b, c);
|
|
|
|
}
|
|
|
|
static void gmp_mpz_fdiv_q_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
|
|
|
|
mpz_fdiv_q_ui(a, b, c);
|
|
|
|
}
|
|
|
|
static void gmp_mpz_fdiv_r_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
|
|
|
|
mpz_fdiv_r_ui(a, b, c);
|
|
|
|
}
|
|
|
|
static void gmp_mpz_cdiv_r_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
|
|
|
|
mpz_cdiv_r_ui(a, b, c);
|
|
|
|
}
|
|
|
|
static void gmp_mpz_cdiv_q_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
|
|
|
|
mpz_cdiv_q_ui(a, b, c);
|
|
|
|
}
|
|
|
|
static void gmp_mpz_mod_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
|
|
|
|
mpz_mod_ui(a, b, c);
|
|
|
|
}
|
|
|
|
static void gmp_mpz_gcd_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
|
|
|
|
mpz_gcd_ui(a, b, c);
|
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
/* Binary operations */
|
|
|
|
#define gmp_binary_ui_op(op, uop) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop, 0)
|
|
|
|
#define gmp_binary_op(op) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, NULL, 0)
|
|
|
|
#define gmp_binary_ui_op_no_zero(op, uop) \
|
|
|
|
_gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop, 1)
|
|
|
|
|
|
|
|
/* Unary operations */
|
|
|
|
#define gmp_unary_op(op) _gmp_unary_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
|
|
|
|
#define gmp_unary_opl(op) _gmp_unary_opl(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static void gmp_free_object_storage(zend_object *obj) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
2014-05-07 00:44:28 +08:00
|
|
|
gmp_object *intern = GET_GMP_OBJECT_FROM_OBJ(obj);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
|
|
|
mpz_clear(intern->num);
|
2014-12-14 06:06:14 +08:00
|
|
|
zend_object_std_dtor(&intern->std);
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static inline zend_object *gmp_create_object_ex(zend_class_entry *ce, mpz_ptr *gmpnum_target) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
2015-02-04 20:24:13 +08:00
|
|
|
gmp_object *intern = emalloc(sizeof(gmp_object) + zend_object_properties_size(ce));
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
zend_object_std_init(&intern->std, ce);
|
2013-06-17 23:49:24 +08:00
|
|
|
object_properties_init(&intern->std, ce);
|
|
|
|
|
|
|
|
mpz_init(intern->num);
|
|
|
|
*gmpnum_target = intern->num;
|
2022-09-14 17:12:19 +08:00
|
|
|
intern->std.handlers = &gmp_object_handlers;
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2014-05-07 00:44:28 +08:00
|
|
|
return &intern->std;
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static zend_object *gmp_create_object(zend_class_entry *ce) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
|
|
|
mpz_ptr gmpnum_dummy;
|
2014-12-14 06:06:14 +08:00
|
|
|
return gmp_create_object_ex(ce, &gmpnum_dummy);
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static inline void gmp_create(zval *target, mpz_ptr *gmpnum_target) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
2014-12-14 06:06:14 +08:00
|
|
|
ZVAL_OBJ(target, gmp_create_object_ex(gmp_ce, gmpnum_target));
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2022-06-26 07:00:19 +08:00
|
|
|
static zend_result gmp_cast_object(zend_object *readobj, zval *writeobj, int type) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
|
|
|
mpz_ptr gmpnum;
|
|
|
|
switch (type) {
|
|
|
|
case IS_STRING:
|
2019-01-31 23:47:58 +08:00
|
|
|
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_strval(writeobj, gmpnum, 10);
|
|
|
|
return SUCCESS;
|
2014-08-26 01:24:55 +08:00
|
|
|
case IS_LONG:
|
2019-01-31 23:47:58 +08:00
|
|
|
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
|
2014-08-26 01:24:55 +08:00
|
|
|
ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
|
2013-06-17 23:49:24 +08:00
|
|
|
return SUCCESS;
|
|
|
|
case IS_DOUBLE:
|
2019-01-31 23:47:58 +08:00
|
|
|
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
|
2013-06-17 23:49:24 +08:00
|
|
|
ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
|
|
|
|
return SUCCESS;
|
2017-12-25 20:18:45 +08:00
|
|
|
case _IS_NUMBER:
|
2019-01-31 23:47:58 +08:00
|
|
|
gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
|
2017-12-25 20:18:45 +08:00
|
|
|
if (mpz_fits_slong_p(gmpnum)) {
|
|
|
|
ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
|
|
|
|
} else {
|
|
|
|
ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
|
|
|
|
}
|
|
|
|
return SUCCESS;
|
2013-06-17 23:49:24 +08:00
|
|
|
default:
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2019-01-31 23:47:58 +08:00
|
|
|
static HashTable *gmp_get_debug_info(zend_object *obj, int *is_temp) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
2014-12-14 06:06:14 +08:00
|
|
|
HashTable *ht, *props = zend_std_get_properties(obj);
|
2019-01-31 23:47:58 +08:00
|
|
|
mpz_ptr gmpnum = GET_GMP_OBJECT_FROM_OBJ(obj)->num;
|
2014-05-07 00:44:28 +08:00
|
|
|
zval zv;
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2013-10-30 03:58:30 +08:00
|
|
|
*is_temp = 1;
|
2015-02-14 03:20:39 +08:00
|
|
|
ht = zend_array_dup(props);
|
2013-10-30 03:58:30 +08:00
|
|
|
|
2014-05-07 00:44:28 +08:00
|
|
|
gmp_strval(&zv, gmpnum, 10);
|
|
|
|
zend_hash_str_update(ht, "num", sizeof("num")-1, &zv);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
|
|
|
return ht;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2019-01-31 23:47:58 +08:00
|
|
|
static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
2019-01-31 23:47:58 +08:00
|
|
|
gmp_object *old_object = GET_GMP_OBJECT_FROM_OBJ(obj);
|
|
|
|
gmp_object *new_object = GET_GMP_OBJECT_FROM_OBJ(gmp_create_object(obj->ce));
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
zend_objects_clone_members( &new_object->std, &old_object->std);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
|
|
|
mpz_set(new_object->num, old_object->num);
|
|
|
|
|
2014-05-07 00:44:28 +08:00
|
|
|
return &new_object->std;
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-05-25 23:12:52 +08:00
|
|
|
static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, zend_uchar opcode) {
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long shift = zval_get_long(op2);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2014-05-07 00:44:28 +08:00
|
|
|
if (shift < 0) {
|
2020-05-25 23:12:52 +08:00
|
|
|
zend_throw_error(
|
|
|
|
zend_ce_value_error, "%s must be greater than or equal to 0",
|
|
|
|
opcode == ZEND_POW ? "Exponent" : "Shift"
|
|
|
|
);
|
|
|
|
ZVAL_UNDEF(return_value);
|
|
|
|
return;
|
2013-06-17 23:49:24 +08:00
|
|
|
} else {
|
|
|
|
mpz_ptr gmpnum_op, gmpnum_result;
|
|
|
|
gmp_temp_t temp;
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_op, op1, temp, 1);
|
2013-06-17 23:49:24 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2014-09-03 00:19:51 +08:00
|
|
|
op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 20:45:24 +08:00
|
|
|
#define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \
|
|
|
|
gmp_zval_binary_ui_op( \
|
|
|
|
result, op1, op2, op, uop, check_b_zero, /* is_operator */ true); \
|
|
|
|
if (UNEXPECTED(EG(exception))) { return FAILURE; } \
|
2013-06-17 23:49:24 +08:00
|
|
|
return SUCCESS;
|
|
|
|
|
|
|
|
#define DO_BINARY_UI_OP(op) DO_BINARY_UI_OP_EX(op, op ## _ui, 0)
|
|
|
|
#define DO_BINARY_OP(op) DO_BINARY_UI_OP_EX(op, NULL, 0)
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
#define DO_UNARY_OP(op) \
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_zval_unary_op(result, op1, op); \
|
2020-08-08 00:15:25 +08:00
|
|
|
if (UNEXPECTED(EG(exception))) { \
|
|
|
|
return FAILURE; \
|
|
|
|
} \
|
2013-06-17 23:49:24 +08:00
|
|
|
return SUCCESS;
|
|
|
|
|
2022-06-26 07:00:19 +08:00
|
|
|
static zend_result gmp_do_operation_ex(zend_uchar opcode, zval *result, zval *op1, zval *op2) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
|
|
|
switch (opcode) {
|
|
|
|
case ZEND_ADD:
|
|
|
|
DO_BINARY_UI_OP(mpz_add);
|
|
|
|
case ZEND_SUB:
|
|
|
|
DO_BINARY_UI_OP(mpz_sub);
|
|
|
|
case ZEND_MUL:
|
|
|
|
DO_BINARY_UI_OP(mpz_mul);
|
2013-11-19 15:36:06 +08:00
|
|
|
case ZEND_POW:
|
2020-05-25 23:12:52 +08:00
|
|
|
shift_operator_helper(mpz_pow_ui, result, op1, op2, opcode);
|
2013-11-19 15:36:06 +08:00
|
|
|
return SUCCESS;
|
2013-06-17 23:49:24 +08:00
|
|
|
case ZEND_DIV:
|
2020-04-15 16:28:48 +08:00
|
|
|
DO_BINARY_UI_OP_EX(mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1);
|
2013-06-17 23:49:24 +08:00
|
|
|
case ZEND_MOD:
|
2020-04-15 16:28:48 +08:00
|
|
|
DO_BINARY_UI_OP_EX(mpz_mod, gmp_mpz_mod_ui, 1);
|
2013-06-17 23:49:24 +08:00
|
|
|
case ZEND_SL:
|
2020-05-25 23:12:52 +08:00
|
|
|
shift_operator_helper(mpz_mul_2exp, result, op1, op2, opcode);
|
2013-06-17 23:49:24 +08:00
|
|
|
return SUCCESS;
|
|
|
|
case ZEND_SR:
|
2020-05-25 23:12:52 +08:00
|
|
|
shift_operator_helper(mpz_fdiv_q_2exp, result, op1, op2, opcode);
|
2013-06-17 23:49:24 +08:00
|
|
|
return SUCCESS;
|
|
|
|
case ZEND_BW_OR:
|
|
|
|
DO_BINARY_OP(mpz_ior);
|
|
|
|
case ZEND_BW_AND:
|
|
|
|
DO_BINARY_OP(mpz_and);
|
|
|
|
case ZEND_BW_XOR:
|
|
|
|
DO_BINARY_OP(mpz_xor);
|
|
|
|
case ZEND_BW_NOT:
|
|
|
|
DO_UNARY_OP(mpz_com);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2022-06-26 07:00:19 +08:00
|
|
|
static zend_result gmp_do_operation(zend_uchar opcode, zval *result, zval *op1, zval *op2) /* {{{ */
|
2014-08-28 04:15:20 +08:00
|
|
|
{
|
|
|
|
zval op1_copy;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (result == op1) {
|
|
|
|
ZVAL_COPY_VALUE(&op1_copy, op1);
|
|
|
|
op1 = &op1_copy;
|
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
retval = gmp_do_operation_ex(opcode, result, op1, op2);
|
2014-08-28 04:15:20 +08:00
|
|
|
|
|
|
|
if (retval == SUCCESS && op1 == &op1_copy) {
|
2018-07-05 18:37:59 +08:00
|
|
|
zval_ptr_dtor(op1);
|
2014-08-28 04:15:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2019-10-07 22:57:49 +08:00
|
|
|
static int gmp_compare(zval *op1, zval *op2) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
2019-10-07 22:57:49 +08:00
|
|
|
zval result;
|
|
|
|
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_cmp(&result, op1, op2, /* is_operator */ true);
|
2020-12-30 00:50:37 +08:00
|
|
|
|
|
|
|
/* An error/exception occurs if one of the operands is not a numeric string
|
|
|
|
* or an object which is different from GMP */
|
|
|
|
if (EG(exception)) {
|
2019-10-07 22:57:49 +08:00
|
|
|
return 1;
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
2020-12-30 00:50:37 +08:00
|
|
|
/* result can only be a zend_long if gmp_cmp hasn't thrown an Error */
|
|
|
|
ZEND_ASSERT(Z_TYPE(result) == IS_LONG);
|
2019-10-07 22:57:49 +08:00
|
|
|
return Z_LVAL(result);
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static int gmp_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
2013-11-24 07:43:12 +08:00
|
|
|
mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(object);
|
2013-10-30 03:58:30 +08:00
|
|
|
smart_str buf = {0};
|
2014-05-07 00:44:28 +08:00
|
|
|
zval zv;
|
2018-02-19 02:07:25 +08:00
|
|
|
php_serialize_data_t serialize_data;
|
2013-10-30 03:58:30 +08:00
|
|
|
|
2014-04-28 16:01:54 +08:00
|
|
|
PHP_VAR_SERIALIZE_INIT(serialize_data);
|
2015-01-03 17:22:58 +08:00
|
|
|
|
2014-05-07 00:44:28 +08:00
|
|
|
gmp_strval(&zv, gmpnum, 10);
|
2014-12-14 06:06:14 +08:00
|
|
|
php_var_serialize(&buf, &zv, &serialize_data);
|
2018-07-05 00:22:24 +08:00
|
|
|
zval_ptr_dtor_str(&zv);
|
2013-10-30 03:58:30 +08:00
|
|
|
|
2019-01-31 23:47:58 +08:00
|
|
|
ZVAL_ARR(&zv, zend_std_get_properties(Z_OBJ_P(object)));
|
2014-12-14 06:06:14 +08:00
|
|
|
php_var_serialize(&buf, &zv, &serialize_data);
|
2013-10-30 03:58:30 +08:00
|
|
|
|
2014-04-28 16:01:54 +08:00
|
|
|
PHP_VAR_SERIALIZE_DESTROY(serialize_data);
|
2015-06-30 09:05:24 +08:00
|
|
|
*buffer = (unsigned char *) estrndup(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
|
|
|
|
*buf_len = ZSTR_LEN(buf.s);
|
2018-05-28 21:27:12 +08:00
|
|
|
zend_string_release_ex(buf.s, 0);
|
2013-11-24 07:43:12 +08:00
|
|
|
|
|
|
|
return SUCCESS;
|
2013-10-30 03:58:30 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static int gmp_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data) /* {{{ */
|
2013-10-30 03:58:30 +08:00
|
|
|
{
|
2013-11-24 07:43:12 +08:00
|
|
|
mpz_ptr gmpnum;
|
2013-10-30 03:58:30 +08:00
|
|
|
const unsigned char *p, *max;
|
2015-09-02 03:40:10 +08:00
|
|
|
zval *zv;
|
2013-11-24 07:43:12 +08:00
|
|
|
int retval = FAILURE;
|
2018-02-19 02:07:25 +08:00
|
|
|
php_unserialize_data_t unserialize_data;
|
2019-01-31 23:47:58 +08:00
|
|
|
zend_object *zobj;
|
2013-10-30 03:58:30 +08:00
|
|
|
|
2014-04-28 16:01:54 +08:00
|
|
|
PHP_VAR_UNSERIALIZE_INIT(unserialize_data);
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_create(object, &gmpnum);
|
2013-10-30 03:58:30 +08:00
|
|
|
|
2019-01-31 23:47:58 +08:00
|
|
|
zobj = Z_OBJ_P(object);
|
2016-07-26 05:02:54 +08:00
|
|
|
|
2013-11-24 07:43:12 +08:00
|
|
|
p = buf;
|
|
|
|
max = buf + buf_len;
|
2013-10-30 03:58:30 +08:00
|
|
|
|
2015-09-02 03:40:10 +08:00
|
|
|
zv = var_tmp_var(&unserialize_data);
|
|
|
|
if (!php_var_unserialize(zv, &p, max, &unserialize_data)
|
|
|
|
|| Z_TYPE_P(zv) != IS_STRING
|
2020-08-08 00:15:25 +08:00
|
|
|
|| convert_to_gmp(gmpnum, zv, 10, 0) == FAILURE
|
2013-06-17 23:49:24 +08:00
|
|
|
) {
|
2014-12-14 06:06:14 +08:00
|
|
|
zend_throw_exception(NULL, "Could not unserialize number", 0);
|
2013-10-30 03:58:30 +08:00
|
|
|
goto exit;
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
|
2015-09-02 03:40:10 +08:00
|
|
|
zv = var_tmp_var(&unserialize_data);
|
|
|
|
if (!php_var_unserialize(zv, &p, max, &unserialize_data)
|
|
|
|
|| Z_TYPE_P(zv) != IS_ARRAY
|
2013-10-30 03:58:30 +08:00
|
|
|
) {
|
2014-12-14 06:06:14 +08:00
|
|
|
zend_throw_exception(NULL, "Could not unserialize properties", 0);
|
2013-10-30 03:58:30 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2015-09-02 03:40:10 +08:00
|
|
|
if (zend_hash_num_elements(Z_ARRVAL_P(zv)) != 0) {
|
2013-10-30 03:58:30 +08:00
|
|
|
zend_hash_copy(
|
2019-01-31 23:47:58 +08:00
|
|
|
zend_std_get_properties(zobj), Z_ARRVAL_P(zv),
|
2014-05-07 00:44:28 +08:00
|
|
|
(copy_ctor_func_t) zval_add_ref
|
2013-10-30 03:58:30 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-11-24 07:43:12 +08:00
|
|
|
retval = SUCCESS;
|
2013-10-30 03:58:30 +08:00
|
|
|
exit:
|
2014-04-28 16:01:54 +08:00
|
|
|
PHP_VAR_UNSERIALIZE_DESTROY(unserialize_data);
|
2013-11-24 07:43:12 +08:00
|
|
|
return retval;
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ ZEND_GINIT_FUNCTION */
|
2006-06-16 02:33:09 +08:00
|
|
|
static ZEND_GINIT_FUNCTION(gmp)
|
2003-11-19 12:44:06 +08:00
|
|
|
{
|
2014-10-17 21:51:21 +08:00
|
|
|
#if defined(COMPILE_DL_GMP) && defined(ZTS)
|
2015-02-17 00:19:32 +08:00
|
|
|
ZEND_TSRMLS_CACHE_UPDATE();
|
2014-10-17 21:51:21 +08:00
|
|
|
#endif
|
2003-11-19 12:44:06 +08:00
|
|
|
gmp_globals->rand_initialized = 0;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ ZEND_MINIT_FUNCTION */
|
2013-06-17 23:49:24 +08:00
|
|
|
ZEND_MINIT_FUNCTION(gmp)
|
2000-11-27 02:36:16 +08:00
|
|
|
{
|
2021-02-05 20:46:46 +08:00
|
|
|
gmp_ce = register_class_GMP();
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_ce->create_object = gmp_create_object;
|
2013-11-24 07:43:12 +08:00
|
|
|
gmp_ce->serialize = gmp_serialize;
|
|
|
|
gmp_ce->unserialize = gmp_unserialize;
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2018-05-31 16:57:22 +08:00
|
|
|
memcpy(&gmp_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
|
2014-05-07 00:44:28 +08:00
|
|
|
gmp_object_handlers.offset = XtOffsetOf(gmp_object, std);
|
|
|
|
gmp_object_handlers.free_obj = gmp_free_object_storage;
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_object_handlers.cast_object = gmp_cast_object;
|
2013-10-30 03:58:30 +08:00
|
|
|
gmp_object_handlers.get_debug_info = gmp_get_debug_info;
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_object_handlers.clone_obj = gmp_clone_obj;
|
|
|
|
gmp_object_handlers.do_operation = gmp_do_operation;
|
|
|
|
gmp_object_handlers.compare = gmp_compare;
|
|
|
|
|
2022-06-06 20:38:03 +08:00
|
|
|
register_gmp_symbols(module_number);
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2000-11-27 02:36:16 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ ZEND_RSHUTDOWN_FUNCTION */
|
2003-11-19 12:44:06 +08:00
|
|
|
ZEND_MODULE_DEACTIVATE_D(gmp)
|
|
|
|
{
|
|
|
|
if (GMPG(rand_initialized)) {
|
|
|
|
gmp_randclear(GMPG(rand_state));
|
|
|
|
GMPG(rand_initialized) = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ ZEND_MINFO_FUNCTION */
|
2001-08-11 10:46:57 +08:00
|
|
|
ZEND_MODULE_INFO_D(gmp)
|
2000-11-27 02:36:16 +08:00
|
|
|
{
|
|
|
|
php_info_print_table_start();
|
2002-06-09 20:56:27 +08:00
|
|
|
php_info_print_table_row(2, "gmp support", "enabled");
|
2009-12-10 10:25:47 +08:00
|
|
|
#ifdef mpir_version
|
|
|
|
php_info_print_table_row(2, "MPIR version", mpir_version);
|
|
|
|
#else
|
2007-04-26 21:44:02 +08:00
|
|
|
php_info_print_table_row(2, "GMP version", gmp_version);
|
2009-12-10 10:25:47 +08:00
|
|
|
#endif
|
2000-11-27 02:36:16 +08:00
|
|
|
php_info_print_table_end();
|
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2022-09-06 18:37:04 +08:00
|
|
|
static zend_result convert_zstr_to_gmp(mpz_t gmp_number, const zend_string *val, zend_long base, uint32_t arg_pos)
|
|
|
|
{
|
|
|
|
const char *num_str = ZSTR_VAL(val);
|
|
|
|
bool skip_lead = false;
|
|
|
|
|
|
|
|
if (ZSTR_LEN(val) >= 2 && num_str[0] == '0') {
|
|
|
|
if ((base == 0 || base == 16) && (num_str[1] == 'x' || num_str[1] == 'X')) {
|
|
|
|
base = 16;
|
|
|
|
skip_lead = true;
|
2022-09-13 18:33:09 +08:00
|
|
|
} else if ((base == 0 || base == 8) && (num_str[1] == 'o' || num_str[1] == 'O')) {
|
|
|
|
base = 8;
|
|
|
|
skip_lead = true;
|
2022-09-06 18:37:04 +08:00
|
|
|
} else if ((base == 0 || base == 2) && (num_str[1] == 'b' || num_str[1] == 'B')) {
|
|
|
|
base = 2;
|
|
|
|
skip_lead = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int gmp_ret = mpz_set_str(gmp_number, (skip_lead ? &num_str[2] : num_str), (int) base);
|
|
|
|
if (-1 == gmp_ret) {
|
|
|
|
if (arg_pos == 0) {
|
|
|
|
zend_value_error("Number is not an integer string");
|
|
|
|
} else {
|
|
|
|
zend_argument_value_error(arg_pos, "is not an integer string");
|
|
|
|
}
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2001-06-05 21:12:10 +08:00
|
|
|
/* {{{ convert_to_gmp
|
|
|
|
* Convert zval to be gmp number */
|
2020-12-29 09:31:15 +08:00
|
|
|
static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, uint32_t arg_pos)
|
2000-11-27 02:36:16 +08:00
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
switch (Z_TYPE_P(val)) {
|
2014-08-26 01:24:55 +08:00
|
|
|
case IS_LONG:
|
2020-09-16 17:04:56 +08:00
|
|
|
mpz_set_si(gmpnumber, Z_LVAL_P(val));
|
2013-06-17 23:49:24 +08:00
|
|
|
return SUCCESS;
|
|
|
|
case IS_STRING: {
|
2022-09-06 18:37:04 +08:00
|
|
|
return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos);
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
2020-09-16 17:04:56 +08:00
|
|
|
default: {
|
|
|
|
zend_long lval;
|
2020-11-30 23:45:48 +08:00
|
|
|
if (!zend_parse_arg_long_slow(val, &lval, arg_pos)) {
|
2021-06-10 20:45:24 +08:00
|
|
|
if (arg_pos == 0) {
|
|
|
|
zend_type_error(
|
|
|
|
"Number must be of type GMP|string|int, %s given", zend_zval_type_name(val));
|
|
|
|
} else {
|
|
|
|
zend_argument_type_error(arg_pos,
|
|
|
|
"must be of type GMP|string|int, %s given", zend_zval_type_name(val));
|
|
|
|
}
|
2020-08-08 00:15:25 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
2020-09-16 17:04:56 +08:00
|
|
|
|
|
|
|
mpz_set_si(gmpnumber, lval);
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
Fix for bugs #10133 and #15454.
Bug #15454 results from a bug in GMP. If you pass in a string '0xABCD' and
specify a base of 0, GMP figures out that it is hex and skips over the 0x
characters. If you specify base 16, then it doesn't skip those chars.
This was confirmed with the following test program:
#include <stdio.h>
#include <gmp.h>
int main()
{
char *str_one, *str_two;
mpz_t num_one, num_two;
mpz_init_set_str (num_one, "0x45", 0);
str_one = mpz_get_str(NULL, 10, num_one);
mpz_init_set_str (num_two, "0x45", 16);
str_two = mpz_get_str(NULL, 10, num_two);
printf("%s / %s\n", str_one, str_two);
mpz_clear (num_one);
mpz_clear (num_two);
return 0;
}
We now take anything that starts with 0[xX] as hexidecimal and anything
that starts 0[bB] as binary (this is what GMP does internally). We also
no longer force the base to 10 or 16, but instead let GMP decide what the
best base is, be it hex, dec, or octal.
2002-02-11 07:12:57 +08:00
|
|
|
}
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2015-02-05 01:12:56 +08:00
|
|
|
static void gmp_strval(zval *result, mpz_t gmpnum, int base) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
2014-09-03 00:42:39 +08:00
|
|
|
size_t num_len;
|
2014-05-07 00:44:28 +08:00
|
|
|
zend_string *str;
|
2000-11-29 23:49:18 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
num_len = mpz_sizeinbase(gmpnum, abs(base));
|
|
|
|
if (mpz_sgn(gmpnum) < 0) {
|
|
|
|
num_len++;
|
|
|
|
}
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
str = zend_string_alloc(num_len, 0);
|
2015-06-30 09:05:24 +08:00
|
|
|
mpz_get_str(ZSTR_VAL(str), base, gmpnum);
|
2015-01-03 17:22:58 +08:00
|
|
|
|
|
|
|
/*
|
2013-06-17 23:49:24 +08:00
|
|
|
* From GMP documentation for mpz_sizeinbase():
|
|
|
|
* The returned value will be exact or 1 too big. If base is a power of
|
|
|
|
* 2, the returned value will always be exact.
|
|
|
|
*
|
|
|
|
* So let's check to see if we already have a \0 byte...
|
|
|
|
*/
|
|
|
|
|
2015-06-30 09:05:24 +08:00
|
|
|
if (ZSTR_VAL(str)[ZSTR_LEN(str) - 1] == '\0') {
|
|
|
|
ZSTR_LEN(str)--;
|
2013-06-17 23:49:24 +08:00
|
|
|
} else {
|
2015-06-30 09:05:24 +08:00
|
|
|
ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
2000-11-29 23:49:18 +08:00
|
|
|
|
2014-09-19 19:41:01 +08:00
|
|
|
ZVAL_NEW_STR(result, str);
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
|
|
|
|
2021-06-10 20:45:24 +08:00
|
|
|
static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operator) /* {{{ */
|
2013-06-17 23:49:24 +08:00
|
|
|
{
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_b;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
2021-01-15 19:30:54 +08:00
|
|
|
bool use_si = 0;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long res;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2021-06-10 20:45:24 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
if (Z_TYPE_P(b_arg) == IS_LONG) {
|
2013-06-17 23:49:24 +08:00
|
|
|
use_si = 1;
|
|
|
|
temp_b.is_used = 0;
|
|
|
|
} else {
|
2021-06-10 20:45:24 +08:00
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (use_si) {
|
2014-08-26 01:24:55 +08:00
|
|
|
res = mpz_cmp_si(gmpnum_a, Z_LVAL_P(b_arg));
|
2013-06-17 23:49:24 +08:00
|
|
|
} else {
|
|
|
|
res = mpz_cmp(gmpnum_a, gmpnum_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
RETURN_LONG(res);
|
2013-06-17 23:49:24 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
/* {{{ gmp_zval_binary_ui_op
|
2000-11-27 02:36:16 +08:00
|
|
|
Execute GMP binary operation.
|
|
|
|
*/
|
2021-06-10 20:45:24 +08:00
|
|
|
static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator)
|
2001-08-08 01:57:55 +08:00
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2021-06-10 20:45:24 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
|
2019-04-12 20:32:29 +08:00
|
|
|
gmpnum_b = NULL;
|
2013-06-17 23:49:24 +08:00
|
|
|
temp_b.is_used = 0;
|
2000-11-27 02:36:16 +08:00
|
|
|
} else {
|
2021-06-10 20:45:24 +08:00
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
if (check_b_zero) {
|
2005-04-25 20:18:50 +08:00
|
|
|
int b_is_zero = 0;
|
2019-04-12 20:32:29 +08:00
|
|
|
if (!gmpnum_b) {
|
2014-08-26 01:24:55 +08:00
|
|
|
b_is_zero = (Z_LVAL_P(b_arg) == 0);
|
2005-04-25 20:18:50 +08:00
|
|
|
} else {
|
2013-06-17 23:49:24 +08:00
|
|
|
b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
|
2005-04-25 20:18:50 +08:00
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
if (b_is_zero) {
|
2020-08-08 00:15:25 +08:00
|
|
|
if ((gmp_binary_op_t) mpz_mod == gmp_op) {
|
|
|
|
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
|
|
|
|
} else {
|
|
|
|
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
|
|
|
|
}
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2005-04-25 20:18:50 +08:00
|
|
|
}
|
2004-02-16 01:22:57 +08:00
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2003-11-19 13:00:56 +08:00
|
|
|
|
2019-04-12 20:32:29 +08:00
|
|
|
if (!gmpnum_b) {
|
2014-09-03 00:19:51 +08:00
|
|
|
gmp_ui_op(gmpnum_result, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
|
2000-11-27 02:36:16 +08:00
|
|
|
} else {
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_op(gmpnum_result, gmpnum_a, gmpnum_b);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
/* {{{ gmp_zval_binary_ui_op2
|
2000-11-27 02:36:16 +08:00
|
|
|
Execute GMP binary operation which returns 2 values.
|
|
|
|
*/
|
2014-12-14 06:06:14 +08:00
|
|
|
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)
|
2001-08-08 01:57:55 +08:00
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result1, gmpnum_result2;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
2014-05-07 00:44:28 +08:00
|
|
|
zval result1, result2;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2003-11-19 13:00:56 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
|
2019-04-12 20:32:29 +08:00
|
|
|
gmpnum_b = NULL;
|
2013-06-17 23:49:24 +08:00
|
|
|
temp_b.is_used = 0;
|
2000-11-27 02:36:16 +08:00
|
|
|
} else {
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
if (check_b_zero) {
|
2005-04-25 20:18:50 +08:00
|
|
|
int b_is_zero = 0;
|
2019-04-12 20:32:29 +08:00
|
|
|
if (!gmpnum_b) {
|
2014-08-26 01:24:55 +08:00
|
|
|
b_is_zero = (Z_LVAL_P(b_arg) == 0);
|
2005-04-25 20:18:50 +08:00
|
|
|
} else {
|
2013-06-17 23:49:24 +08:00
|
|
|
b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
|
2005-04-25 20:18:50 +08:00
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
if (b_is_zero) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2005-04-25 20:18:50 +08:00
|
|
|
}
|
2004-02-16 01:22:57 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_create(&result1, &gmpnum_result1);
|
|
|
|
gmp_create(&result2, &gmpnum_result2);
|
2014-05-07 00:44:28 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
array_init(return_value);
|
2014-05-07 00:44:28 +08:00
|
|
|
add_next_index_zval(return_value, &result1);
|
|
|
|
add_next_index_zval(return_value, &result2);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2019-04-12 20:32:29 +08:00
|
|
|
if (!gmpnum_b) {
|
2014-09-03 00:19:51 +08:00
|
|
|
gmp_ui_op(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
|
2000-11-27 02:36:16 +08:00
|
|
|
} else {
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_op(gmpnum_result1, gmpnum_result2, gmpnum_a, gmpnum_b);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ _gmp_binary_ui_op */
|
2013-06-17 23:49:24 +08:00
|
|
|
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)
|
2003-11-19 13:00:56 +08:00
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg, *b_arg;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_zval_binary_ui_op(
|
|
|
|
return_value, a_arg, b_arg, gmp_op, gmp_ui_op, check_b_zero, /* is_operator */ false);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
|
|
|
/* Unary operations */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ gmp_zval_unary_op */
|
2014-12-14 06:06:14 +08:00
|
|
|
static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op)
|
2001-08-08 01:57:55 +08:00
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_ptr gmpnum_a, gmpnum_result;
|
|
|
|
gmp_temp_t temp_a;
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
|
|
|
gmp_op(gmpnum_result, gmpnum_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ _gmp_unary_op */
|
2003-11-19 13:00:56 +08:00
|
|
|
static inline void _gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_op_t gmp_op)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_zval_unary_op(return_value, a_arg, gmp_op);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ _gmp_unary_opl */
|
2003-11-19 13:00:56 +08:00
|
|
|
static inline void _gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_opl_t gmp_op)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
|
|
|
mpz_ptr gmpnum_a;
|
|
|
|
gmp_temp_t temp_a;
|
2000-11-29 23:49:18 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-29 23:49:18 +08:00
|
|
|
}
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2014-08-26 01:24:55 +08:00
|
|
|
RETVAL_LONG(gmp_op(gmpnum_a));
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-29 23:49:18 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2000-11-29 23:49:18 +08:00
|
|
|
|
2023-01-05 07:28:36 +08:00
|
|
|
static bool gmp_verify_base(zend_long base, uint32_t arg_num)
|
|
|
|
{
|
|
|
|
if (base && (base < 2 || base > GMP_MAX_BASE)) {
|
|
|
|
zend_argument_value_error(arg_num, "must be between 2 and %d", GMP_MAX_BASE);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static zend_result gmp_initialize_number(mpz_ptr gmp_number, const zend_string *arg_str, zend_long arg_l, zend_long base)
|
|
|
|
{
|
|
|
|
if (arg_str) {
|
|
|
|
return convert_zstr_to_gmp(gmp_number, arg_str, base, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
mpz_set_si(gmp_number, arg_l);
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Initializes GMP number */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_init)
|
|
|
|
{
|
2022-09-06 18:37:04 +08:00
|
|
|
mpz_ptr gmp_number;
|
|
|
|
zend_string *arg_str = NULL;
|
|
|
|
zend_long arg_l = 0;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long base = 0;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2022-09-06 18:37:04 +08:00
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 2)
|
|
|
|
Z_PARAM_STR_OR_LONG(arg_str, arg_l)
|
|
|
|
Z_PARAM_OPTIONAL
|
|
|
|
Z_PARAM_LONG(base)
|
|
|
|
ZEND_PARSE_PARAMETERS_END();
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2023-01-05 07:28:36 +08:00
|
|
|
if (!gmp_verify_base(base, 2)) {
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2001-08-05 22:48:17 +08:00
|
|
|
}
|
|
|
|
|
2022-09-06 18:37:04 +08:00
|
|
|
INIT_GMP_RETVAL(gmp_number);
|
2023-01-05 07:28:36 +08:00
|
|
|
|
|
|
|
if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
|
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
int gmp_import_export_validate(zend_long size, zend_long options, int *order, int *endian)
|
2014-08-28 04:45:05 +08:00
|
|
|
{
|
|
|
|
if (size < 1) {
|
2020-08-08 00:15:25 +08:00
|
|
|
/* size argument is in second position */
|
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 1");
|
2014-08-28 04:45:05 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (options & (GMP_LSW_FIRST | GMP_MSW_FIRST)) {
|
|
|
|
case GMP_LSW_FIRST:
|
|
|
|
*order = -1;
|
|
|
|
break;
|
|
|
|
case GMP_MSW_FIRST:
|
|
|
|
case 0: /* default */
|
|
|
|
*order = 1;
|
|
|
|
break;
|
|
|
|
default:
|
2020-08-08 00:15:25 +08:00
|
|
|
/* options argument is in second position */
|
|
|
|
zend_argument_value_error(3, "cannot use multiple word order options");
|
2014-08-28 04:45:05 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (options & (GMP_LITTLE_ENDIAN | GMP_BIG_ENDIAN | GMP_NATIVE_ENDIAN)) {
|
|
|
|
case GMP_LITTLE_ENDIAN:
|
|
|
|
*endian = -1;
|
|
|
|
break;
|
|
|
|
case GMP_BIG_ENDIAN:
|
|
|
|
*endian = 1;
|
|
|
|
break;
|
|
|
|
case GMP_NATIVE_ENDIAN:
|
|
|
|
case 0: /* default */
|
|
|
|
*endian = 0;
|
|
|
|
break;
|
|
|
|
default:
|
2020-08-08 00:15:25 +08:00
|
|
|
/* options argument is in second position */
|
|
|
|
zend_argument_value_error(3, "cannot use multiple endian options");
|
2014-08-28 04:45:05 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Imports a GMP number from a binary string */
|
2014-08-28 04:45:05 +08:00
|
|
|
ZEND_FUNCTION(gmp_import)
|
|
|
|
{
|
|
|
|
char *data;
|
2014-09-02 20:28:07 +08:00
|
|
|
size_t data_len;
|
|
|
|
zend_long size = 1;
|
2014-09-02 22:40:41 +08:00
|
|
|
zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
|
2014-08-28 04:45:05 +08:00
|
|
|
int order, endian;
|
|
|
|
mpz_ptr gmpnumber;
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &data, &data_len, &size, &options) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2014-08-28 04:45:05 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (gmp_import_export_validate(size, options, &order, &endian) == FAILURE) {
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2014-08-28 04:45:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((data_len % size) != 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(1, "must be a multiple of argument #2 ($word_size)");
|
|
|
|
RETURN_THROWS();
|
2014-08-28 04:45:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
INIT_GMP_RETVAL(gmpnumber);
|
|
|
|
|
|
|
|
mpz_import(gmpnumber, data_len / size, order, size, endian, 0, data);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Exports a GMP number to a binary string */
|
2014-08-28 04:45:05 +08:00
|
|
|
ZEND_FUNCTION(gmp_export)
|
|
|
|
{
|
|
|
|
zval *gmpnumber_arg;
|
2014-09-02 20:28:07 +08:00
|
|
|
zend_long size = 1;
|
2014-09-02 22:40:41 +08:00
|
|
|
zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
|
2014-08-28 04:45:05 +08:00
|
|
|
int order, endian;
|
|
|
|
mpz_ptr gmpnumber;
|
|
|
|
gmp_temp_t temp_a;
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|ll", &gmpnumber_arg, &size, &options) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2014-08-28 04:45:05 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (gmp_import_export_validate(size, options, &order, &endian) == FAILURE) {
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2014-08-28 04:45:05 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnumber, gmpnumber_arg, temp_a, 1);
|
2014-08-28 04:45:05 +08:00
|
|
|
|
|
|
|
if (mpz_sgn(gmpnumber) == 0) {
|
2020-10-15 16:16:22 +08:00
|
|
|
RETVAL_EMPTY_STRING();
|
2014-08-28 04:45:05 +08:00
|
|
|
} else {
|
2024-10-14 02:06:31 +08:00
|
|
|
ZEND_ASSERT(size > 0);
|
|
|
|
size_t size_in_base_2 = mpz_sizeinbase(gmpnumber, 2);
|
|
|
|
if (size > ZEND_LONG_MAX / 4 || size_in_base_2 > SIZE_MAX - (size_t) size * 8 + 1) {
|
|
|
|
zend_argument_value_error(2, "is too large for argument #1 ($num)");
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
|
|
|
size_t bits_per_word = (size_t) size * 8;
|
|
|
|
size_t count = (size_in_base_2 + bits_per_word - 1) / bits_per_word;
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2016-04-27 14:04:29 +08:00
|
|
|
zend_string *out_string = zend_string_safe_alloc(count, size, 0, 0);
|
2015-06-30 09:05:24 +08:00
|
|
|
mpz_export(ZSTR_VAL(out_string), NULL, order, size, endian, 0, gmpnumber);
|
2016-04-27 12:23:51 +08:00
|
|
|
ZSTR_VAL(out_string)[ZSTR_LEN(out_string)] = '\0';
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2020-10-15 16:16:22 +08:00
|
|
|
RETVAL_NEW_STR(out_string);
|
2014-08-28 04:45:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Gets signed long value of GMP number */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_intval)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *gmpnumber_arg;
|
2020-09-16 17:04:56 +08:00
|
|
|
mpz_ptr gmpnum;
|
|
|
|
gmp_temp_t temp_a;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &gmpnumber_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2020-09-16 17:04:56 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a, 1);
|
|
|
|
RETVAL_LONG(mpz_get_si(gmpnum));
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Gets string representation of GMP number */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_strval)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *gmpnumber_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long base = 10;
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_ptr gmpnum;
|
|
|
|
gmp_temp_t temp_a;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &base) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2014-09-29 02:39:19 +08:00
|
|
|
/* Although the maximum base in general in GMP is 62, mpz_get_str()
|
2010-09-15 18:51:55 +08:00
|
|
|
* is explicitly limited to -36 when dealing with negative bases. */
|
2014-09-29 02:39:19 +08:00
|
|
|
if ((base < 2 && base > -2) || base > GMP_MAX_BASE || base < -36) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be between 2 and %d, or -2 and -36", GMP_MAX_BASE);
|
|
|
|
RETURN_THROWS();
|
2000-12-05 22:17:38 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a, 1);
|
2007-11-01 08:46:13 +08:00
|
|
|
|
2015-02-05 01:12:56 +08:00
|
|
|
gmp_strval(return_value, gmpnum, (int)base);
|
Fix for bugs #10133 and #15454.
Bug #15454 results from a bug in GMP. If you pass in a string '0xABCD' and
specify a base of 0, GMP figures out that it is hex and skips over the 0x
characters. If you specify base 16, then it doesn't skip those chars.
This was confirmed with the following test program:
#include <stdio.h>
#include <gmp.h>
int main()
{
char *str_one, *str_two;
mpz_t num_one, num_two;
mpz_init_set_str (num_one, "0x45", 0);
str_one = mpz_get_str(NULL, 10, num_one);
mpz_init_set_str (num_two, "0x45", 16);
str_two = mpz_get_str(NULL, 10, num_two);
printf("%s / %s\n", str_one, str_two);
mpz_clear (num_one);
mpz_clear (num_two);
return 0;
}
We now take anything that starts with 0[xX] as hexidecimal and anything
that starts 0[bB] as binary (this is what GMP does internally). We also
no longer force the base to 10 or 16, but instead let GMP decide what the
best base is, be it hex, dec, or octal.
2002-02-11 07:12:57 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Add a and b */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_add)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_binary_ui_op(mpz_add, mpz_add_ui);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Subtract b from a */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_sub)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_binary_ui_op(mpz_sub, mpz_sub_ui);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Multiply a and b */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_mul)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_binary_ui_op(mpz_mul, mpz_mul_ui);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Divide a by b, returns quotient and reminder */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_div_qr)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg, *b_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long round = GMP_ROUND_ZERO;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2003-11-19 13:00:56 +08:00
|
|
|
switch (round) {
|
2000-11-27 02:36:16 +08:00
|
|
|
case GMP_ROUND_ZERO:
|
2020-04-15 16:28:48 +08:00
|
|
|
gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_tdiv_qr, mpz_tdiv_qr_ui, 1);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
|
|
|
case GMP_ROUND_PLUSINF:
|
2020-04-15 16:28:48 +08:00
|
|
|
gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_cdiv_qr, mpz_cdiv_qr_ui, 1);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
|
|
|
case GMP_ROUND_MINUSINF:
|
2020-04-15 16:28:48 +08:00
|
|
|
gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_fdiv_qr, mpz_fdiv_qr_ui, 1);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
2013-06-17 23:49:24 +08:00
|
|
|
default:
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
|
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Divide a by b, returns reminder only */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_div_r)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg, *b_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long round = GMP_ROUND_ZERO;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2003-11-19 13:00:56 +08:00
|
|
|
switch (round) {
|
2000-11-27 02:36:16 +08:00
|
|
|
case GMP_ROUND_ZERO:
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_zval_binary_ui_op(
|
|
|
|
return_value, a_arg, b_arg, mpz_tdiv_r, gmp_mpz_tdiv_r_ui, 1, /* is_operator */ false);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
|
|
|
case GMP_ROUND_PLUSINF:
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_zval_binary_ui_op(
|
|
|
|
return_value, a_arg, b_arg, mpz_cdiv_r, gmp_mpz_cdiv_r_ui, 1, /* is_operator */ false);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
|
|
|
case GMP_ROUND_MINUSINF:
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_zval_binary_ui_op(
|
|
|
|
return_value, a_arg, b_arg, mpz_fdiv_r, gmp_mpz_fdiv_r_ui, 1, /* is_operator */ false);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
2013-06-17 23:49:24 +08:00
|
|
|
default:
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
|
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Divide a by b, returns quotient only */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_div_q)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg, *b_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long round = GMP_ROUND_ZERO;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2003-11-19 13:00:56 +08:00
|
|
|
switch (round) {
|
2000-11-27 02:36:16 +08:00
|
|
|
case GMP_ROUND_ZERO:
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_zval_binary_ui_op(
|
|
|
|
return_value, a_arg, b_arg, mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1, /* is_operator */ false);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
|
|
|
case GMP_ROUND_PLUSINF:
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_zval_binary_ui_op(
|
|
|
|
return_value, a_arg, b_arg, mpz_cdiv_q, gmp_mpz_cdiv_q_ui, 1, /* is_operator */ false);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
|
|
|
case GMP_ROUND_MINUSINF:
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_zval_binary_ui_op(
|
|
|
|
return_value, a_arg, b_arg, mpz_fdiv_q, gmp_mpz_fdiv_q_ui, 1, /* is_operator */ false);
|
2000-11-27 02:36:16 +08:00
|
|
|
break;
|
2013-06-17 23:49:24 +08:00
|
|
|
default:
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
|
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Computes a modulo b */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_mod)
|
|
|
|
{
|
2020-04-15 16:28:48 +08:00
|
|
|
gmp_binary_ui_op_no_zero(mpz_mod, gmp_mpz_mod_ui);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Divide a by b using exact division algorithm */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_divexact)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_binary_ui_op_no_zero(mpz_divexact, NULL);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Negates a number */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_neg)
|
|
|
|
{
|
|
|
|
gmp_unary_op(mpz_neg);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates absolute value */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_abs)
|
|
|
|
{
|
|
|
|
gmp_unary_op(mpz_abs);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates factorial function */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_fact)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
2020-08-08 00:15:25 +08:00
|
|
|
mpz_ptr gmpnum_result;
|
2005-03-01 21:09:33 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2005-03-01 21:09:33 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
if (Z_TYPE_P(a_arg) == IS_LONG) {
|
|
|
|
if (Z_LVAL_P(a_arg) < 0) {
|
|
|
|
zend_argument_value_error(1, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2005-03-01 21:09:33 +08:00
|
|
|
}
|
|
|
|
} else {
|
2020-08-08 00:15:25 +08:00
|
|
|
mpz_ptr gmpnum;
|
|
|
|
gmp_temp_t temp_a;
|
|
|
|
|
|
|
|
FETCH_GMP_ZVAL(gmpnum, a_arg, temp_a, 1);
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
|
|
|
|
if (mpz_sgn(gmpnum) < 0) {
|
|
|
|
zend_argument_value_error(1, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2005-03-01 21:09:33 +08:00
|
|
|
}
|
|
|
|
}
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
|
|
|
mpz_fac_ui(gmpnum_result, zval_get_long(a_arg));
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates binomial coefficient */
|
2017-12-10 04:09:27 +08:00
|
|
|
ZEND_FUNCTION(gmp_binomial)
|
|
|
|
{
|
|
|
|
zval *n_arg;
|
|
|
|
zend_long k;
|
|
|
|
mpz_ptr gmpnum_result;
|
|
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &n_arg, &k) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2017-12-10 04:09:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (k < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2017-12-10 04:09:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
|
|
|
if (Z_TYPE_P(n_arg) == IS_LONG && Z_LVAL_P(n_arg) >= 0) {
|
|
|
|
mpz_bin_uiui(gmpnum_result, (gmp_ulong) Z_LVAL_P(n_arg), (gmp_ulong) k);
|
|
|
|
} else {
|
|
|
|
mpz_ptr gmpnum_n;
|
|
|
|
gmp_temp_t temp_n;
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_n, n_arg, temp_n, 1);
|
2017-12-10 04:09:27 +08:00
|
|
|
mpz_bin_ui(gmpnum_result, gmpnum_n, (gmp_ulong) k);
|
|
|
|
FREE_GMP_TEMP(temp_n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Raise base to power exp */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_pow)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *base_arg;
|
2018-02-19 02:15:36 +08:00
|
|
|
mpz_ptr gmpnum_result;
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_temp_t temp_base;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long exp;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &base_arg, &exp) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2007-11-01 17:25:11 +08:00
|
|
|
if (exp < 0) {
|
2020-05-25 23:12:52 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2024-10-12 14:14:56 +08:00
|
|
|
double powmax = log((double)ZEND_LONG_MAX);
|
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) {
|
2015-04-16 22:00:31 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2024-10-12 14:14:56 +08:00
|
|
|
if ((log(Z_LVAL_P(base_arg)) * exp) > powmax) {
|
|
|
|
zend_value_error("base and exponent overflow");
|
|
|
|
RETURN_THROWS();
|
2024-10-12 14:14:56 +08:00
|
|
|
}
|
2024-10-12 14:14:56 +08:00
|
|
|
mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
|
2000-11-27 02:36:16 +08:00
|
|
|
} else {
|
2018-02-19 02:15:36 +08:00
|
|
|
mpz_ptr gmpnum_base;
|
2024-10-12 14:14:56 +08:00
|
|
|
zend_ulong gmpnum;
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
|
2015-04-16 22:00:31 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2024-10-12 14:14:56 +08:00
|
|
|
gmpnum = mpz_get_ui(gmpnum_base);
|
|
|
|
if ((log(gmpnum) * exp) > powmax) {
|
|
|
|
FREE_GMP_TEMP(temp_base);
|
|
|
|
zend_value_error("base and exponent overflow");
|
|
|
|
RETURN_THROWS();
|
2024-10-12 14:14:56 +08:00
|
|
|
}
|
2024-10-12 14:14:56 +08:00
|
|
|
mpz_pow_ui(gmpnum_result, gmpnum_base, exp);
|
2007-11-01 17:34:18 +08:00
|
|
|
FREE_GMP_TEMP(temp_base);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Raise base to power exp and take result modulo mod */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_powm)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *base_arg, *exp_arg, *mod_arg;
|
|
|
|
mpz_ptr gmpnum_base, gmpnum_exp, gmpnum_mod, gmpnum_result;
|
2003-11-19 13:00:56 +08:00
|
|
|
int use_ui = 0;
|
2013-10-30 03:07:34 +08:00
|
|
|
gmp_temp_t temp_base, temp_exp, temp_mod;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz", &base_arg, &exp_arg, &mod_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
|
2003-11-19 13:00:56 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
if (Z_TYPE_P(exp_arg) == IS_LONG && Z_LVAL_P(exp_arg) >= 0) {
|
2003-11-19 13:00:56 +08:00
|
|
|
use_ui = 1;
|
2013-06-17 23:49:24 +08:00
|
|
|
temp_exp.is_used = 0;
|
2000-11-27 02:36:16 +08:00
|
|
|
} else {
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_exp, exp_arg, temp_exp, temp_base, 2);
|
2013-06-17 23:49:24 +08:00
|
|
|
if (mpz_sgn(gmpnum_exp) < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 0");
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp_base);
|
|
|
|
FREE_GMP_TEMP(temp_exp);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2005-03-01 21:09:33 +08:00
|
|
|
}
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL_DEP_DEP(gmpnum_mod, mod_arg, temp_mod, temp_exp, temp_base, 3);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
if (!mpz_cmp_ui(gmpnum_mod, 0)) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_base);
|
2014-09-05 02:55:09 +08:00
|
|
|
FREE_GMP_TEMP(temp_exp);
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_mod);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2004-02-16 01:22:57 +08:00
|
|
|
}
|
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2003-11-19 13:00:56 +08:00
|
|
|
if (use_ui) {
|
2014-08-26 01:24:55 +08:00
|
|
|
mpz_powm_ui(gmpnum_result, gmpnum_base, (zend_ulong) Z_LVAL_P(exp_arg), gmpnum_mod);
|
2000-11-27 02:36:16 +08:00
|
|
|
} else {
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_powm(gmpnum_result, gmpnum_base, gmpnum_exp, gmpnum_mod);
|
2007-11-01 17:34:18 +08:00
|
|
|
FREE_GMP_TEMP(temp_exp);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_base);
|
|
|
|
FREE_GMP_TEMP(temp_mod);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Takes integer part of square root of a */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_sqrt)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_result;
|
|
|
|
gmp_temp_t temp_a;
|
2005-03-01 21:09:33 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2005-03-01 21:09:33 +08:00
|
|
|
}
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2005-03-01 21:09:33 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
if (mpz_sgn(gmpnum_a) < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(1, "must be greater than or equal to 0");
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2014-08-28 04:45:05 +08:00
|
|
|
}
|
2005-03-01 21:09:33 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
|
|
|
mpz_sqrt(gmpnum_result, gmpnum_a);
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Square root with remainder */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_sqrtrem)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
|
|
|
|
gmp_temp_t temp_a;
|
2014-05-07 00:44:28 +08:00
|
|
|
zval result1, result2;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
|
|
|
if (mpz_sgn(gmpnum_a) < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(1, "must be greater than or equal to 0");
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2005-03-01 21:09:33 +08:00
|
|
|
}
|
2015-01-03 17:22:58 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_create(&result1, &gmpnum_result1);
|
|
|
|
gmp_create(&result2, &gmpnum_result2);
|
2014-05-07 00:44:28 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
array_init(return_value);
|
2014-05-07 00:44:28 +08:00
|
|
|
add_next_index_zval(return_value, &result1);
|
|
|
|
add_next_index_zval(return_value, &result2);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_sqrtrem(gmpnum_result1, gmpnum_result2, gmpnum_a);
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Takes integer part of nth root */
|
2013-11-29 06:42:23 +08:00
|
|
|
ZEND_FUNCTION(gmp_root)
|
|
|
|
{
|
|
|
|
zval *a_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long nth;
|
2013-11-29 06:42:23 +08:00
|
|
|
mpz_ptr gmpnum_a, gmpnum_result;
|
|
|
|
gmp_temp_t temp_a;
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2013-11-29 06:42:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nth <= 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than 0");
|
|
|
|
RETURN_THROWS();
|
2013-11-29 06:42:23 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2013-11-29 06:42:23 +08:00
|
|
|
|
|
|
|
if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be odd if argument #1 ($a) is negative");
|
2013-11-29 06:42:23 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2013-11-29 06:42:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2014-09-03 00:19:51 +08:00
|
|
|
mpz_root(gmpnum_result, gmpnum_a, (gmp_ulong) nth);
|
2013-11-29 06:42:23 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates integer part of nth root and remainder */
|
2013-11-29 06:42:23 +08:00
|
|
|
ZEND_FUNCTION(gmp_rootrem)
|
|
|
|
{
|
|
|
|
zval *a_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long nth;
|
2013-11-29 06:42:23 +08:00
|
|
|
mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
|
|
|
|
gmp_temp_t temp_a;
|
2014-05-07 00:44:28 +08:00
|
|
|
zval result1, result2;
|
2013-11-29 06:42:23 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2013-11-29 06:42:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nth <= 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 1");
|
|
|
|
RETURN_THROWS();
|
2013-11-29 06:42:23 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2013-11-29 06:42:23 +08:00
|
|
|
|
|
|
|
if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be odd if argument #1 ($a) is negative");
|
2013-11-29 06:42:23 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2013-11-29 06:42:23 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_create(&result1, &gmpnum_result1);
|
|
|
|
gmp_create(&result2, &gmpnum_result2);
|
2014-05-07 00:44:28 +08:00
|
|
|
|
2013-11-29 06:42:23 +08:00
|
|
|
array_init(return_value);
|
2014-05-07 00:44:28 +08:00
|
|
|
add_next_index_zval(return_value, &result1);
|
|
|
|
add_next_index_zval(return_value, &result2);
|
2013-11-29 06:42:23 +08:00
|
|
|
|
2015-04-16 22:27:42 +08:00
|
|
|
#if GMP_51_OR_NEWER
|
|
|
|
/* mpz_rootrem() is supported since GMP 4.2, but buggy wrt odd roots
|
|
|
|
* of negative numbers */
|
2014-09-03 00:19:51 +08:00
|
|
|
mpz_rootrem(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) nth);
|
2013-12-03 03:10:08 +08:00
|
|
|
#else
|
2015-04-16 22:31:53 +08:00
|
|
|
mpz_root(gmpnum_result1, gmpnum_a, (gmp_ulong) nth);
|
|
|
|
mpz_pow_ui(gmpnum_result2, gmpnum_result1, (gmp_ulong) nth);
|
2013-12-03 03:10:08 +08:00
|
|
|
mpz_sub(gmpnum_result2, gmpnum_a, gmpnum_result2);
|
|
|
|
#endif
|
2014-08-28 04:45:05 +08:00
|
|
|
|
2013-11-29 06:42:23 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Checks if a is an exact square */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_perfect_square)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
|
|
|
mpz_ptr gmpnum_a;
|
|
|
|
gmp_temp_t temp_a;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
RETVAL_BOOL((mpz_perfect_square_p(gmpnum_a) != 0));
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Checks if a is a perfect power */
|
2017-12-10 04:32:42 +08:00
|
|
|
ZEND_FUNCTION(gmp_perfect_power)
|
|
|
|
{
|
|
|
|
zval *a_arg;
|
|
|
|
mpz_ptr gmpnum_a;
|
|
|
|
gmp_temp_t temp_a;
|
|
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2017-12-10 04:32:42 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2017-12-10 04:32:42 +08:00
|
|
|
|
|
|
|
RETVAL_BOOL((mpz_perfect_power_p(gmpnum_a) != 0));
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Checks if a is "probably prime" */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_prob_prime)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *gmpnumber_arg;
|
|
|
|
mpz_ptr gmpnum_a;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long reps = 10;
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_temp_t temp_a;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &reps) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, gmpnumber_arg, temp_a, 1);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2018-01-02 16:10:27 +08:00
|
|
|
RETVAL_LONG(mpz_probab_prime_p(gmpnum_a, (int)reps));
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Computes greatest common denominator (gcd) of a and b */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_gcd)
|
|
|
|
{
|
2020-04-15 16:28:48 +08:00
|
|
|
gmp_binary_ui_op(mpz_gcd, gmp_mpz_gcd_ui);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Computes least common multiple (lcm) of a and b */
|
2017-12-10 04:22:37 +08:00
|
|
|
ZEND_FUNCTION(gmp_lcm)
|
|
|
|
{
|
2020-04-15 16:28:48 +08:00
|
|
|
gmp_binary_ui_op(mpz_lcm, mpz_lcm_ui);
|
2017-12-10 04:22:37 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Computes G, S, and T, such that AS + BT = G = `gcd' (A, B) */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_gcdext)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg, *b_arg;
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_b, gmpnum_t, gmpnum_s, gmpnum_g;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
2014-05-07 00:44:28 +08:00
|
|
|
zval result_g, result_s, result_t;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_create(&result_g, &gmpnum_g);
|
|
|
|
gmp_create(&result_s, &gmpnum_s);
|
|
|
|
gmp_create(&result_t, &gmpnum_t);
|
2014-05-07 00:44:28 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
array_init(return_value);
|
2014-05-07 00:44:28 +08:00
|
|
|
add_assoc_zval(return_value, "g", &result_g);
|
|
|
|
add_assoc_zval(return_value, "s", &result_s);
|
|
|
|
add_assoc_zval(return_value, "t", &result_t);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_gcdext(gmpnum_g, gmpnum_s, gmpnum_t, gmpnum_a, gmpnum_b);
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Computes the inverse of a modulo b */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_invert)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg, *b_arg;
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
2007-11-01 08:46:13 +08:00
|
|
|
int res;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
|
|
|
|
|
|
|
|
// TODO Early check if b_arg IS_LONG?
|
|
|
|
if (0 == mpz_cmp_ui(gmpnum_b, 0)) {
|
|
|
|
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
|
|
|
res = mpz_invert(gmpnum_result, gmpnum_a, gmpnum_b);
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2013-06-17 23:49:24 +08:00
|
|
|
if (!res) {
|
2018-07-05 16:54:26 +08:00
|
|
|
zval_ptr_dtor(return_value);
|
2000-11-27 02:36:16 +08:00
|
|
|
RETURN_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Computes Jacobi symbol */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_jacobi)
|
|
|
|
{
|
2020-04-15 16:28:48 +08:00
|
|
|
zval *a_arg, *b_arg;
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_b;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
|
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
|
2020-04-15 16:28:48 +08:00
|
|
|
|
|
|
|
RETVAL_LONG(mpz_jacobi(gmpnum_a, gmpnum_b));
|
|
|
|
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Computes Legendre symbol */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_legendre)
|
|
|
|
{
|
2020-04-15 16:28:48 +08:00
|
|
|
zval *a_arg, *b_arg;
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_b;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
|
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
|
2020-04-15 16:28:48 +08:00
|
|
|
|
|
|
|
RETVAL_LONG(mpz_legendre(gmpnum_a, gmpnum_b));
|
|
|
|
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Computes the Kronecker symbol */
|
2017-12-10 04:57:41 +08:00
|
|
|
ZEND_FUNCTION(gmp_kronecker)
|
|
|
|
{
|
|
|
|
zval *a_arg, *b_arg;
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_b;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
2021-01-15 19:30:54 +08:00
|
|
|
bool use_a_si = 0, use_b_si = 0;
|
2017-12-10 04:57:41 +08:00
|
|
|
int result;
|
|
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2017-12-10 04:57:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Z_TYPE_P(a_arg) == IS_LONG && Z_TYPE_P(b_arg) != IS_LONG) {
|
|
|
|
use_a_si = 1;
|
|
|
|
temp_a.is_used = 0;
|
|
|
|
} else {
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2017-12-10 04:57:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Z_TYPE_P(b_arg) == IS_LONG) {
|
|
|
|
use_b_si = 1;
|
|
|
|
temp_b.is_used = 0;
|
|
|
|
} else {
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
|
2017-12-10 04:57:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (use_a_si) {
|
|
|
|
ZEND_ASSERT(use_b_si == 0);
|
|
|
|
result = mpz_si_kronecker((gmp_long) Z_LVAL_P(a_arg), gmpnum_b);
|
|
|
|
} else if (use_b_si) {
|
|
|
|
result = mpz_kronecker_si(gmpnum_a, (gmp_long) Z_LVAL_P(b_arg));
|
|
|
|
} else {
|
|
|
|
result = mpz_kronecker(gmpnum_a, gmpnum_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
|
|
|
|
|
|
|
RETURN_LONG(result);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Compares two numbers */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_cmp)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg, *b_arg;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2021-06-10 20:45:24 +08:00
|
|
|
gmp_cmp(return_value, a_arg, b_arg, /* is_operator */ false);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Gets the sign of the number */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_sign)
|
|
|
|
{
|
2013-11-23 19:54:20 +08:00
|
|
|
/* Can't use gmp_unary_opl here, because mpz_sgn is a macro */
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
|
|
|
mpz_ptr gmpnum_a;
|
|
|
|
gmp_temp_t temp_a;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2013-11-23 19:54:20 +08:00
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
RETVAL_LONG(mpz_sgn(gmpnum_a));
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static void gmp_init_random(void)
|
2014-09-24 05:12:23 +08:00
|
|
|
{
|
|
|
|
if (!GMPG(rand_initialized)) {
|
|
|
|
/* Initialize */
|
|
|
|
gmp_randinit_mt(GMPG(rand_state));
|
|
|
|
/* Seed */
|
|
|
|
gmp_randseed_ui(GMPG(rand_state), GENERATE_SEED());
|
|
|
|
|
|
|
|
GMPG(rand_initialized) = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Seed the RNG */
|
2014-10-20 00:25:05 +08:00
|
|
|
ZEND_FUNCTION(gmp_random_seed)
|
|
|
|
{
|
|
|
|
zval *seed;
|
|
|
|
gmp_temp_t temp_a;
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &seed) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2014-10-20 00:25:05 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_init_random();
|
2014-10-20 00:25:05 +08:00
|
|
|
|
|
|
|
if (Z_TYPE_P(seed) == IS_LONG && Z_LVAL_P(seed) >= 0) {
|
|
|
|
gmp_randseed_ui(GMPG(rand_state), Z_LVAL_P(seed));
|
|
|
|
}
|
|
|
|
else {
|
2018-02-19 02:15:36 +08:00
|
|
|
mpz_ptr gmpnum_seed;
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_seed, seed, temp_a, 1);
|
2014-10-20 00:25:05 +08:00
|
|
|
|
|
|
|
gmp_randseed(GMPG(rand_state), gmpnum_seed);
|
|
|
|
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Gets a random number in the range 0 to (2 ** n) - 1 */
|
2014-09-24 05:12:23 +08:00
|
|
|
ZEND_FUNCTION(gmp_random_bits)
|
|
|
|
{
|
2014-10-22 23:46:13 +08:00
|
|
|
zend_long bits;
|
2014-09-24 05:12:23 +08:00
|
|
|
mpz_ptr gmpnum_result;
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &bits) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2014-09-24 05:12:23 +08:00
|
|
|
}
|
|
|
|
|
2024-10-19 13:01:32 +08:00
|
|
|
#if SIZEOF_SIZE_T == 4
|
|
|
|
const zend_long maxbits = ULONG_MAX / GMP_NUMB_BITS;
|
|
|
|
#else
|
|
|
|
const zend_long maxbits = INT_MAX;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (bits <= 0 || bits > maxbits) {
|
|
|
|
zend_argument_value_error(1, "must be between 1 and " ZEND_LONG_FMT, maxbits);
|
2020-08-08 00:15:25 +08:00
|
|
|
RETURN_THROWS();
|
2014-09-24 05:12:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_init_random();
|
2014-09-24 05:12:23 +08:00
|
|
|
|
2024-10-19 13:01:32 +08:00
|
|
|
mpz_urandomb(gmpnum_result, GMPG(rand_state), (mp_bitcnt_t)bits);
|
2014-09-24 05:12:23 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Gets a random number in the range min to max */
|
2014-09-24 05:12:23 +08:00
|
|
|
ZEND_FUNCTION(gmp_random_range)
|
|
|
|
{
|
|
|
|
zval *min_arg, *max_arg;
|
2018-02-19 02:15:36 +08:00
|
|
|
mpz_ptr gmpnum_max, gmpnum_result;
|
2015-06-12 04:10:08 +08:00
|
|
|
mpz_t gmpnum_range;
|
2014-09-24 05:12:23 +08:00
|
|
|
gmp_temp_t temp_a, temp_b;
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &min_arg, &max_arg) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2014-09-24 05:12:23 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
gmp_init_random();
|
2014-09-25 05:01:09 +08:00
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_max, max_arg, temp_a, 2);
|
2014-09-25 05:01:09 +08:00
|
|
|
|
|
|
|
if (Z_TYPE_P(min_arg) == IS_LONG && Z_LVAL_P(min_arg) >= 0) {
|
|
|
|
if (mpz_cmp_ui(gmpnum_max, Z_LVAL_P(min_arg)) <= 0) {
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
|
|
|
|
RETURN_THROWS();
|
2014-09-25 05:01:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2015-06-12 04:10:08 +08:00
|
|
|
mpz_init(gmpnum_range);
|
2014-09-25 05:01:09 +08:00
|
|
|
|
2015-06-12 04:10:08 +08:00
|
|
|
if (Z_LVAL_P(min_arg) != 0) {
|
|
|
|
mpz_sub_ui(gmpnum_range, gmpnum_max, Z_LVAL_P(min_arg) - 1);
|
|
|
|
} else {
|
|
|
|
mpz_add_ui(gmpnum_range, gmpnum_max, 1);
|
2014-09-25 05:01:09 +08:00
|
|
|
}
|
|
|
|
|
2015-06-12 04:10:08 +08:00
|
|
|
mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
|
2014-09-25 05:01:09 +08:00
|
|
|
|
2015-06-12 04:10:08 +08:00
|
|
|
if (Z_LVAL_P(min_arg) != 0) {
|
2014-09-25 05:01:09 +08:00
|
|
|
mpz_add_ui(gmpnum_result, gmpnum_result, Z_LVAL_P(min_arg));
|
|
|
|
}
|
2014-09-24 05:12:23 +08:00
|
|
|
|
2015-06-12 04:10:08 +08:00
|
|
|
mpz_clear(gmpnum_range);
|
2014-09-24 14:56:46 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2015-06-12 04:10:08 +08:00
|
|
|
} else {
|
2018-02-19 02:15:36 +08:00
|
|
|
mpz_ptr gmpnum_min;
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_min, min_arg, temp_b, temp_a, 1);
|
2014-09-24 14:56:46 +08:00
|
|
|
|
2014-09-25 05:01:09 +08:00
|
|
|
if (mpz_cmp(gmpnum_max, gmpnum_min) <= 0) {
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
|
|
|
|
RETURN_THROWS();
|
2014-09-25 05:01:09 +08:00
|
|
|
}
|
2014-09-24 05:12:23 +08:00
|
|
|
|
2014-09-25 05:01:09 +08:00
|
|
|
INIT_GMP_RETVAL(gmpnum_result);
|
2015-06-12 04:10:08 +08:00
|
|
|
mpz_init(gmpnum_range);
|
2014-09-24 05:12:23 +08:00
|
|
|
|
2015-06-12 04:10:08 +08:00
|
|
|
mpz_sub(gmpnum_range, gmpnum_max, gmpnum_min);
|
|
|
|
mpz_add_ui(gmpnum_range, gmpnum_range, 1);
|
|
|
|
mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
|
2014-09-25 05:01:09 +08:00
|
|
|
mpz_add(gmpnum_result, gmpnum_result, gmpnum_min);
|
|
|
|
|
2015-06-12 04:10:08 +08:00
|
|
|
mpz_clear(gmpnum_range);
|
2014-09-25 05:01:09 +08:00
|
|
|
FREE_GMP_TEMP(temp_b);
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
}
|
2014-09-24 05:12:23 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates logical AND of a and b */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_and)
|
|
|
|
{
|
|
|
|
gmp_binary_op(mpz_and);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates logical OR of a and b */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_or)
|
|
|
|
{
|
|
|
|
gmp_binary_op(mpz_ior);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates one's complement of a */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_com)
|
|
|
|
{
|
|
|
|
gmp_unary_op(mpz_com);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Finds next prime of a */
|
2006-07-18 22:54:32 +08:00
|
|
|
ZEND_FUNCTION(gmp_nextprime)
|
|
|
|
{
|
2021-06-29 16:04:10 +08:00
|
|
|
gmp_unary_op(mpz_nextprime);
|
2006-07-18 22:54:32 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates logical exclusive OR of a and b */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_xor)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
gmp_binary_op(mpz_xor);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Sets or clear bit in a */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_setbit)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long index;
|
2021-01-15 19:30:54 +08:00
|
|
|
bool set = 1;
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_ptr gmpnum_a;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|b", &a_arg, gmp_ce, &index, &set) == FAILURE) {
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2007-04-19 04:36:54 +08:00
|
|
|
if (index < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2007-04-19 04:36:54 +08:00
|
|
|
}
|
2021-06-29 16:04:10 +08:00
|
|
|
if (index / GMP_NUMB_BITS >= INT_MAX) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be less than %d * %d", INT_MAX, GMP_NUMB_BITS);
|
|
|
|
RETURN_THROWS();
|
2021-06-29 16:04:10 +08:00
|
|
|
}
|
2007-04-19 04:36:54 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
|
|
|
|
|
2003-11-19 13:00:56 +08:00
|
|
|
if (set) {
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_setbit(gmpnum_a, index);
|
2000-11-27 02:36:16 +08:00
|
|
|
} else {
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_clrbit(gmpnum_a, index);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Clears bit in a */
|
2000-11-27 02:36:16 +08:00
|
|
|
ZEND_FUNCTION(gmp_clrbit)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long index;
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_ptr gmpnum_a;
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &a_arg, gmp_ce, &index) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
|
2007-04-19 04:36:54 +08:00
|
|
|
if (index < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2007-04-19 04:36:54 +08:00
|
|
|
}
|
2000-11-27 02:36:16 +08:00
|
|
|
|
2013-06-17 23:49:24 +08:00
|
|
|
gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
|
|
|
|
mpz_clrbit(gmpnum_a, index);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Tests if bit is set in a */
|
2007-11-02 01:51:34 +08:00
|
|
|
ZEND_FUNCTION(gmp_testbit)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long index;
|
2013-06-17 23:49:24 +08:00
|
|
|
mpz_ptr gmpnum_a;
|
2014-03-10 19:06:40 +08:00
|
|
|
gmp_temp_t temp_a;
|
2007-11-02 01:51:34 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &index) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2007-11-02 01:51:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (index < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2007-11-02 01:51:34 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2014-03-10 19:06:40 +08:00
|
|
|
RETVAL_BOOL(mpz_tstbit(gmpnum_a, index));
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
2007-11-02 01:51:34 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates the population count of a */
|
2000-11-29 23:49:18 +08:00
|
|
|
ZEND_FUNCTION(gmp_popcount)
|
|
|
|
{
|
2020-04-15 16:28:48 +08:00
|
|
|
gmp_unary_opl(mpz_popcount);
|
2000-11-29 23:49:18 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Calculates hamming distance between a and b */
|
2000-11-29 23:49:18 +08:00
|
|
|
ZEND_FUNCTION(gmp_hamdist)
|
|
|
|
{
|
2020-04-15 16:28:48 +08:00
|
|
|
zval *a_arg, *b_arg;
|
|
|
|
mpz_ptr gmpnum_a, gmpnum_b;
|
|
|
|
gmp_temp_t temp_a, temp_b;
|
|
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
|
|
|
FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
|
2020-04-15 16:28:48 +08:00
|
|
|
|
|
|
|
RETVAL_LONG(mpz_hamdist(gmpnum_a, gmpnum_b));
|
|
|
|
|
|
|
|
FREE_GMP_TEMP(temp_a);
|
|
|
|
FREE_GMP_TEMP(temp_b);
|
2000-11-29 23:49:18 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Finds first zero bit */
|
2000-11-29 23:49:18 +08:00
|
|
|
ZEND_FUNCTION(gmp_scan0)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
|
|
|
mpz_ptr gmpnum_a;
|
|
|
|
gmp_temp_t temp_a;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long start;
|
2000-11-29 23:49:18 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-29 23:49:18 +08:00
|
|
|
}
|
|
|
|
|
2007-11-01 08:46:13 +08:00
|
|
|
if (start < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2007-04-19 04:53:28 +08:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2013-06-17 23:49:24 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
RETVAL_LONG(mpz_scan0(gmpnum_a, start));
|
2007-11-01 08:46:13 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-29 23:49:18 +08:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 21:32:55 +08:00
|
|
|
/* {{{ Finds first non-zero bit */
|
2000-11-29 23:49:18 +08:00
|
|
|
ZEND_FUNCTION(gmp_scan1)
|
|
|
|
{
|
2013-06-17 23:49:24 +08:00
|
|
|
zval *a_arg;
|
|
|
|
mpz_ptr gmpnum_a;
|
|
|
|
gmp_temp_t temp_a;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_long start;
|
2000-11-29 23:49:18 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
|
2019-12-31 01:20:30 +08:00
|
|
|
RETURN_THROWS();
|
2000-11-29 23:49:18 +08:00
|
|
|
}
|
|
|
|
|
2007-11-01 08:46:13 +08:00
|
|
|
if (start < 0) {
|
2020-08-08 00:15:25 +08:00
|
|
|
zend_argument_value_error(2, "must be greater than or equal to 0");
|
|
|
|
RETURN_THROWS();
|
2007-04-19 04:53:28 +08:00
|
|
|
}
|
2000-11-29 23:49:18 +08:00
|
|
|
|
2020-08-08 00:15:25 +08:00
|
|
|
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
|
2001-07-31 13:44:11 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
RETVAL_LONG(mpz_scan1(gmpnum_a, start));
|
2013-06-17 23:49:24 +08:00
|
|
|
FREE_GMP_TEMP(temp_a);
|
2000-11-27 02:36:16 +08:00
|
|
|
}
|
2001-06-05 21:12:10 +08:00
|
|
|
/* }}} */
|
2021-11-18 18:11:52 +08:00
|
|
|
|
2023-01-05 07:28:36 +08:00
|
|
|
ZEND_METHOD(GMP, __construct)
|
|
|
|
{
|
|
|
|
zend_string *arg_str = NULL;
|
|
|
|
zend_long arg_l = 0;
|
|
|
|
zend_long base = 0;
|
|
|
|
|
|
|
|
ZEND_PARSE_PARAMETERS_START(0, 2)
|
|
|
|
Z_PARAM_OPTIONAL
|
|
|
|
Z_PARAM_STR_OR_LONG(arg_str, arg_l)
|
|
|
|
Z_PARAM_LONG(base)
|
|
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
|
|
|
|
if (!gmp_verify_base(base, 2)) {
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
|
|
|
|
|
|
|
return_value = ZEND_THIS;
|
|
|
|
mpz_ptr gmp_number = GET_GMP_FROM_ZVAL(ZEND_THIS);
|
|
|
|
|
|
|
|
if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-18 18:11:52 +08:00
|
|
|
ZEND_METHOD(GMP, __serialize)
|
|
|
|
{
|
|
|
|
ZEND_PARSE_PARAMETERS_NONE();
|
|
|
|
|
|
|
|
zval zv;
|
|
|
|
array_init(return_value);
|
|
|
|
|
|
|
|
mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(ZEND_THIS);
|
|
|
|
gmp_strval(&zv, gmpnum, 16);
|
|
|
|
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
|
|
|
|
|
|
|
|
HashTable *props = Z_OBJ_P(ZEND_THIS)->properties;
|
|
|
|
if (props && zend_hash_num_elements(props) != 0) {
|
|
|
|
ZVAL_ARR(&zv, zend_proptable_to_symtable(
|
|
|
|
zend_std_get_properties(Z_OBJ_P(ZEND_THIS)), /* always duplicate */ 1));
|
|
|
|
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ZEND_METHOD(GMP, __unserialize)
|
|
|
|
{
|
|
|
|
HashTable *data;
|
|
|
|
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 1)
|
|
|
|
Z_PARAM_ARRAY_HT(data)
|
|
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
|
|
|
|
zval *num = zend_hash_index_find(data, 0);
|
|
|
|
if (!num || Z_TYPE_P(num) != IS_STRING ||
|
|
|
|
convert_to_gmp(GET_GMP_FROM_ZVAL(ZEND_THIS), num, 16, 0) == FAILURE) {
|
|
|
|
zend_throw_exception(NULL, "Could not unserialize number", 0);
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
|
|
|
|
|
|
|
zval *props = zend_hash_index_find(data, 1);
|
|
|
|
if (props) {
|
|
|
|
if (Z_TYPE_P(props) != IS_ARRAY) {
|
|
|
|
zend_throw_exception(NULL, "Could not unserialize properties", 0);
|
|
|
|
RETURN_THROWS();
|
|
|
|
}
|
|
|
|
|
|
|
|
object_properties_load(Z_OBJ_P(ZEND_THIS), Z_ARRVAL_P(props));
|
|
|
|
}
|
|
|
|
}
|