diff options
author | Stanislav Malyshev <stas@php.net> | 2000-11-26 18:36:16 +0000 |
---|---|---|
committer | Stanislav Malyshev <stas@php.net> | 2000-11-26 18:36:16 +0000 |
commit | dca467faf377b2fb56525b1dc1055f3081ea2c74 (patch) | |
tree | 339891177d0a776fdea3a6e6dedf3e71ffdf2788 /ext | |
parent | d4fc424554b8eb358827f28e7ddeb6c218e6b7e9 (diff) | |
download | php-git-dca467faf377b2fb56525b1dc1055f3081ea2c74.tar.gz |
GNU GMP - arbitrary precision nubers library
Diffstat (limited to 'ext')
-rw-r--r-- | ext/gmp/Makefile.in | 9 | ||||
-rw-r--r-- | ext/gmp/README | 3 | ||||
-rw-r--r-- | ext/gmp/TODO | 26 | ||||
-rw-r--r-- | ext/gmp/config.m4 | 27 | ||||
-rw-r--r-- | ext/gmp/gmp.c | 1086 | ||||
-rw-r--r-- | ext/gmp/php_gmp.h | 113 |
6 files changed, 1264 insertions, 0 deletions
diff --git a/ext/gmp/Makefile.in b/ext/gmp/Makefile.in new file mode 100644 index 0000000000..5a63346585 --- /dev/null +++ b/ext/gmp/Makefile.in @@ -0,0 +1,9 @@ +# $Id$ + +LTLIBRARY_NAME = libgmp.la +LTLIBRARY_SOURCES = gmp.c +LTLIBRARY_SHARED_NAME = gmp.la +LTLIBRARY_LIBADD = $(GMP_LIBADD) +LTLIBRARY_SHARED_LIBADD = $(GMP_SHARED_LIBADD) + +include $(top_srcdir)/build/dynlib.mk diff --git a/ext/gmp/README b/ext/gmp/README new file mode 100644 index 0000000000..6c1bf0b847 --- /dev/null +++ b/ext/gmp/README @@ -0,0 +1,3 @@ +Arbitrary length number support with GNU GMP library. +Documentation will follow. +See also http://www.swox.com/gmp/.
\ No newline at end of file diff --git a/ext/gmp/TODO b/ext/gmp/TODO new file mode 100644 index 0000000000..faa1008577 --- /dev/null +++ b/ext/gmp/TODO @@ -0,0 +1,26 @@ +mpz_mul_2exp +mpz_[ft]div_[qr]_2exp +mpz_popcount +mpz_hamdist +mpz_scan0 +mpz_scan1 + +V 3: +mpz_nextprime +mpz_addmul +mpz_root +mpz_perfect_power_p +mpz_lcm +mpz_si_kronecker +mpz_kronecker_si +mpz_remove +mpz_bin_ui +mpz_fib_ui +mpz_cmpabs +mpz_xor +mpz_tstbit +mpz_urandom[bm] +mpz_fits_slong_p +mpz_mul_si +mpz_odd_p +mpz_even_p
\ No newline at end of file diff --git a/ext/gmp/config.m4 b/ext/gmp/config.m4 new file mode 100644 index 0000000000..245c7e3503 --- /dev/null +++ b/ext/gmp/config.m4 @@ -0,0 +1,27 @@ +dnl $Id$ +dnl config.m4 for extension gmp + +dnl If your extension references something external, use with: + +PHP_ARG_WITH(gmp, for gmp support, +dnl Make sure that the comment is aligned: +[ --with-gmp Include gmp support]) + +if test "$PHP_GMP" != "no"; then + + for i in /usr/local /usr $PHP_GMP; do + if test -f $i/include/gmp.h; then + GMP_DIR=$i + fi + done + + if test -z "$GMP_DIR"; then + AC_MSG_ERROR(Unable to locate gmp.h) + fi + AC_ADD_INCLUDE($GMP_DIR/include) + + + PHP_EXTENSION(gmp, $ext_shared) + AC_DEFINE(HAVE_GMP, 1, [ ]) + AC_ADD_LIBRARY_WITH_PATH(gmp, $GMP_DIR/lib) +fi diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c new file mode 100644 index 0000000000..a148ff4e24 --- /dev/null +++ b/ext/gmp/gmp.c @@ -0,0 +1,1086 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | 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. | + +----------------------------------------------------------------------+ + | Authors: Stanislav Malyshev <stas@php.net> | + +----------------------------------------------------------------------+ + */ + +#include "php.h" +#include "php_ini.h" +#include "php_gmp.h" +#include "ext/standard/info.h" + +/* You should tweak config.m4 so this symbol (or some else suitable) + gets defined. +*/ +#if HAVE_GMP + +#include <gmp.h> +/* If you declare any globals in php_gmp.h uncomment this: +ZEND_DECLARE_MODULE_GLOBALS(gmp) +*/ + +/* True global resources - no need for thread safety here */ +static int le_gmp; + +static unsigned char first_of_two_force_ref[] = { 2, BYREF_FORCE, BYREF_NONE }; + +/* Every user visible function must have an entry in gmp_functions[]. +*/ +function_entry gmp_functions[] = { + ZEND_FE(gmp_init, NULL) + ZEND_FE(gmp_intval, NULL) + ZEND_FE(gmp_strval, NULL) + ZEND_FE(gmp_add, NULL) + ZEND_FE(gmp_sub, NULL) + ZEND_FE(gmp_mul, NULL) + ZEND_FE(gmp_div_qr, NULL) + ZEND_FE(gmp_div_q, NULL) + ZEND_FE(gmp_div_r, NULL) + ZEND_FALIAS(gmp_div, gmp_div_q, NULL) + ZEND_FE(gmp_mod, NULL) + ZEND_FE(gmp_divexact, NULL) + ZEND_FE(gmp_neg, NULL) + ZEND_FE(gmp_abs, NULL) + ZEND_FE(gmp_fact, NULL) + ZEND_FE(gmp_sqrt, NULL) + ZEND_FE(gmp_sqrtrem, NULL) + ZEND_FE(gmp_pow, NULL) + ZEND_FE(gmp_powm, NULL) + ZEND_FE(gmp_perfect_square, NULL) + ZEND_FE(gmp_prob_prime, NULL) + ZEND_FE(gmp_gcd, NULL) + ZEND_FE(gmp_gcdext, NULL) + ZEND_FE(gmp_invert, NULL) + ZEND_FE(gmp_jacobi, NULL) + ZEND_FE(gmp_legendre, NULL) + ZEND_FE(gmp_cmp, NULL) + ZEND_FE(gmp_sign, NULL) + ZEND_FE(gmp_random, NULL) + ZEND_FE(gmp_and, NULL) + ZEND_FE(gmp_or, NULL) + ZEND_FE(gmp_com, NULL) + ZEND_FE(gmp_xor, NULL) + ZEND_FE(gmp_setbit, first_of_two_force_ref) + ZEND_FE(gmp_clrbit, first_of_two_force_ref) + {NULL, NULL, NULL} /* Must be the last line in gmp_functions[] */ +}; + +zend_module_entry gmp_module_entry = { + "gmp", + gmp_functions, + ZEND_MINIT(gmp), + ZEND_MSHUTDOWN(gmp), + NULL, /* Replace with NULL if there's nothing to do at request start */ + NULL, /* Replace with NULL if there's nothing to do at request end */ + ZEND_MINFO(gmp), + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_GMP +ZEND_GET_MODULE(gmp) +#endif + +static void _php_gmpnum_free(zend_rsrc_list_entry *rsrc); + +#define GMP_RESOURCE_NAME "GMP integer" + +#define GMP_ROUND_ZERO 0 +#define GMP_ROUND_PLUSINF 1 +#define GMP_ROUND_MINUSINF 2 + +ZEND_MINIT_FUNCTION(gmp) +{ +/* Remove comments if you have entries in php.ini + REGISTER_INI_ENTRIES(); +*/ + le_gmp = zend_register_list_destructors_ex(_php_gmpnum_free, NULL, + GMP_RESOURCE_NAME, + module_number); + REGISTER_LONG_CONSTANT("GMP_ROUND_ZERO", GMP_ROUND_ZERO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("GMP_ROUND_PLUSINF", GMP_ROUND_PLUSINF, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("GMP_ROUND_MINUSINF", GMP_ROUND_MINUSINF, CONST_CS | CONST_PERSISTENT); + return SUCCESS; +} + +ZEND_MSHUTDOWN_FUNCTION(gmp) +{ +/* Remove comments if you have entries in php.ini + UNREGISTER_INI_ENTRIES(); +*/ + return SUCCESS; +} + +ZEND_MINFO_FUNCTION(gmp) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "gmp support", "enabled"); + php_info_print_table_end(); + + /* Remove comments if you have entries in php.ini + DISPLAY_INI_ENTRIES(); + */ +} + +/* Fetch zval to be GMP number. + Initially, zval can be also number or string */ +#define FETCH_GMP_ZVAL(gmpnumber, zval) \ +if(Z_TYPE_PP(zval) == IS_RESOURCE) { \ + ZEND_FETCH_RESOURCE(gmpnumber, mpz_t *, zval, -1, GMP_RESOURCE_NAME, le_gmp);\ +} else {\ + convert_to_gmp(&gmpnumber,zval);\ + ZEND_REGISTER_RESOURCE(NULL, gmpnumber, le_gmp);\ +} + +/* create a new initialized GMP number */ +#define INIT_GMP_NUM(gmpnumber) { gmpnumber=emalloc(sizeof(mpz_t)); mpz_init(*gmpnumber); } +#define FREE_GMP_NUM(gmpnumber) { mpz_clear(*gmpnumber); efree(gmpnumber); } + +/* Convert zval to be gmp number */ +static int convert_to_gmp(mpz_t * *gmpnumber, zval **val) +{ + int ret = 0; + + *gmpnumber = emalloc(sizeof(mpz_t)); + switch(Z_TYPE_PP(val)) { + case IS_LONG: + case IS_BOOL: + case IS_CONSTANT: + { + convert_to_long_ex(val); + mpz_init_set_si(**gmpnumber, Z_LVAL_PP(val)); + } + break; + case IS_STRING: + { + char *numstr = Z_STRVAL_PP(val); + if(numstr[0] == '0' && (numstr[1] == 'x' || numstr[1] == 'X')) { + ret = mpz_init_set_str(**gmpnumber, numstr+2, 16); + } else { + ret = mpz_init_set_str(**gmpnumber, numstr, 10); + } + } + break; + default: + zend_error(E_WARNING,"Unable to convert variable to GMP - wrong type"); + return FAILURE; + } + + return ret?FAILURE:SUCCESS; +} + +typedef void (*gmp_unary_op_t)(mpz_ptr, mpz_srcptr); +typedef void (*gmp_unary_ui_op_t)(mpz_ptr, unsigned long); + +typedef void (*gmp_binary_op_t)(mpz_ptr, mpz_srcptr, mpz_srcptr); +typedef unsigned long (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, unsigned long); +typedef void (*gmp_binary_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr); +typedef unsigned long (*gmp_binary_ui_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, unsigned long); +#define gmp_zval_binary_ui_op(r,a,b,o,u) gmp_zval_binary_ui_op_ex(r,a,b,o,u,0) +#define gmp_zval_binary_ui_op2(r,a,b,o,u) gmp_zval_binary_ui_op2_ex(r,a,b,o,u,0) + +#define gmp_binary_ui_op(op,uop) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop) +#define gmp_binary_op(op) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, NULL) + +/* Unary operations */ +#define gmp_unary_op(op) _gmp_unary_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op) +#define gmp_unary_ui_op(op) _gmp_unary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op) + +/* + Execute GMP binary operation. + May return GMP resource or long if operation allows this +*/ +static inline void gmp_zval_binary_ui_op_ex(zval *return_value, zval **a_arg, zval **b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, int allow_ui_return) { + mpz_t *gmpnum_a, *gmpnum_b, *gmpnum_result; + unsigned long long_result; + int use_ui=0; + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + if(gmp_ui_op && Z_TYPE_PP(b_arg) == IS_LONG && Z_LVAL_PP(b_arg) >= 0) { + use_ui=1; + } else { + FETCH_GMP_ZVAL(gmpnum_b, b_arg); + } + + INIT_GMP_NUM(gmpnum_result); + if(use_ui && gmp_ui_op) { + if(allow_ui_return) { + long_result = gmp_ui_op(*gmpnum_result, *gmpnum_a, (unsigned long)Z_LVAL_PP(b_arg)); + } else { + gmp_ui_op(*gmpnum_result, *gmpnum_a, (unsigned long)Z_LVAL_PP(b_arg)); + } + } else { + gmp_op(*gmpnum_result, *gmpnum_a, *gmpnum_b); + } + + if(use_ui && allow_ui_return) { + FREE_GMP_NUM(gmpnum_result); + RETURN_LONG((long)long_result); + } else { + ZEND_REGISTER_RESOURCE(return_value, gmpnum_result, le_gmp); + } +} + +/* + Execute GMP binary operation which returns 2 values. + May return GMP resources or longs if operation allows this. +*/ +static inline void gmp_zval_binary_ui_op2_ex(zval *return_value, zval **a_arg, zval **b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int allow_ui_return) { + mpz_t *gmpnum_a, *gmpnum_b, *gmpnum_result1, *gmpnum_result2; + zval r; + int use_ui=0; + unsigned long long_result; + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + if(gmp_ui_op && Z_TYPE_PP(b_arg) == IS_LONG && Z_LVAL_PP(b_arg) >= 0) { + /* use _ui function */ + use_ui=1; + } else { + FETCH_GMP_ZVAL(gmpnum_b, b_arg); + } + + INIT_GMP_NUM(gmpnum_result1); + INIT_GMP_NUM(gmpnum_result2); + + if(use_ui && gmp_ui_op) { + if(allow_ui_return) { + long_result = gmp_ui_op(*gmpnum_result1, *gmpnum_result2, *gmpnum_a, (unsigned long)Z_LVAL_PP(b_arg)); + } else { + gmp_ui_op(*gmpnum_result1, *gmpnum_result2, *gmpnum_a, (unsigned long)Z_LVAL_PP(b_arg)); + } + } else { + gmp_op(*gmpnum_result1, *gmpnum_result2, *gmpnum_a, *gmpnum_b); + } + + array_init(return_value); + ZEND_REGISTER_RESOURCE(&r, gmpnum_result1, le_gmp); + add_index_resource(return_value, 0, Z_LVAL(r)); + if(use_ui && allow_ui_return) { + mpz_clear(*gmpnum_result2); + add_index_long(return_value, 1, long_result); + } else { + ZEND_REGISTER_RESOURCE(&r, gmpnum_result2, le_gmp); + add_index_resource(return_value, 1, Z_LVAL(r)); + } +} + + +static inline void _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op) { + zval **a_arg, **b_arg; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + gmp_zval_binary_ui_op(return_value,a_arg,b_arg,gmp_op,gmp_ui_op); +} + +/* Unary operations */ + +static inline void gmp_zval_unary_op(zval *return_value, zval **a_arg, gmp_unary_op_t gmp_op) { + mpz_t *gmpnum_a, *gmpnum_result; + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + + INIT_GMP_NUM(gmpnum_result); + gmp_op(*gmpnum_result, *gmpnum_a); + + ZEND_REGISTER_RESOURCE(return_value, gmpnum_result, le_gmp); +} + +static inline void gmp_zval_unary_ui_op(zval *return_value, zval **a_arg, gmp_unary_ui_op_t gmp_op) { + mpz_t *gmpnum_result; + + convert_to_long_ex(a_arg); + + INIT_GMP_NUM(gmpnum_result); + gmp_op(*gmpnum_result, Z_LVAL_PP(a_arg)); + + ZEND_REGISTER_RESOURCE(return_value, gmpnum_result, le_gmp); +} + +/* + Execute GMP unary operation. +*/ +static inline void _gmp_unary_ui_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_ui_op_t gmp_op) { + zval **a_arg; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &a_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + gmp_zval_unary_ui_op(return_value,a_arg,gmp_op); +} + +static inline void _gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_op_t gmp_op) { + zval **a_arg; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &a_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + gmp_zval_unary_op(return_value,a_arg,gmp_op); +} + + +/* Remove the following function when you have succesfully modified config.m4 + so that your module can be compiled into PHP, it exists only for testing + purposes. */ + +/* {{{ proto resource gmp_init(mixed number) + Initialize GMP number + */ +ZEND_FUNCTION(gmp_init) +{ + zval **number_arg; + mpz_t * gmpnumber; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &number_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + if(convert_to_gmp(&gmpnumber,number_arg) == FAILURE) { + RETURN_FALSE; + } + + /* Write your own code here to handle argument number. */ + ZEND_REGISTER_RESOURCE(return_value, gmpnumber, le_gmp); +} +/* }}} */ + +/* {{{ proto int gmp_intval(resource gmpnumber) + Get signed long value of GMP number + */ +ZEND_FUNCTION(gmp_intval) +{ + zval **gmpnumber_arg; + mpz_t * gmpnum; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &gmpnumber_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + if(Z_TYPE_PP(gmpnumber_arg) == IS_RESOURCE) { + ZEND_FETCH_RESOURCE(gmpnum, mpz_t *, gmpnumber_arg, -1, GMP_RESOURCE_NAME, le_gmp); + RETVAL_LONG(mpz_get_si(*gmpnum)); + } else { + convert_to_long_ex(gmpnumber_arg); + RETVAL_LONG(Z_LVAL_PP(gmpnumber_arg)); + } +} +/* }}} */ + +/* {{{ proto string gmp_strval(resource gmpnumber [, int base]) + Get string representation of GMP number + */ +ZEND_FUNCTION(gmp_strval) +{ + zval **gmpnumber_arg, **base_arg; + int base, num_len, argc; + mpz_t * gmpnum; + char *out_string; + + argc = ZEND_NUM_ARGS(); + if (argc < 1 || argc > 2 || zend_get_parameters_ex(argc, &gmpnumber_arg, &base_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg); + + switch (argc) { + case 2: + convert_to_long_ex(base_arg); + base = Z_LVAL_PP(base_arg); + break; + case 1: + base = 10; + break; + } + + num_len = mpz_sizeinbase(*gmpnum, base); + out_string = emalloc(num_len+2); + if(mpz_sgn(*gmpnum) < 0) { + num_len++; + } + mpz_get_str(out_string, base, *gmpnum); + + RETVAL_STRINGL(out_string, num_len, 0); +} +/* }}} */ + +/* {{{ proto resource gmp_add(resource a, resource b) + Add a and b + */ +ZEND_FUNCTION(gmp_add) +{ + gmp_binary_ui_op(mpz_add, (gmp_binary_ui_op_t)mpz_add_ui); +} +/* }}} */ + +/* {{{ proto resource gmp_sub(resource a, resource b) + Subtract b from a + */ +ZEND_FUNCTION(gmp_sub) +{ + gmp_binary_ui_op(mpz_sub, (gmp_binary_ui_op_t)mpz_sub_ui); +} +/* }}} */ + +/* {{{ proto resource gmp_mul(resource a, resource b) + Multiply a and b + */ +ZEND_FUNCTION(gmp_mul) +{ + gmp_binary_ui_op(mpz_mul, (gmp_binary_ui_op_t)mpz_mul_ui); +} +/* }}} */ + +/* {{{ proto array gmp_div_qr(resource a, resource b [, int round]) + Divide a by b, return quotient and reminder. + */ +ZEND_FUNCTION(gmp_div_qr) +{ + zval **a_arg, **b_arg, **round_arg; + int round, argc; + + argc = ZEND_NUM_ARGS(); + if (argc < 2 || argc > 3 || zend_get_parameters_ex(argc, &a_arg, &b_arg, &round_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + switch (argc) { + case 3: + convert_to_long_ex(round_arg); + round = Z_LVAL_PP(round_arg); + break; + case 2: + round = GMP_ROUND_ZERO; + break; + } + + switch(round) { + case GMP_ROUND_ZERO: + gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_tdiv_qr, (gmp_binary_ui_op2_t)mpz_tdiv_qr_ui); + break; + case GMP_ROUND_PLUSINF: + gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_cdiv_qr, (gmp_binary_ui_op2_t)mpz_cdiv_qr_ui); + break; + case GMP_ROUND_MINUSINF: + gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_fdiv_qr, (gmp_binary_ui_op2_t)mpz_fdiv_qr_ui); + break; + } + +} +/* }}} */ + +/* {{{ proto array gmp_div_r(resource a, resource b [, int round]) + Divide a by b, return reminder only + */ +ZEND_FUNCTION(gmp_div_r) +{ + zval **a_arg, **b_arg, **round_arg; + int round, argc; + + argc = ZEND_NUM_ARGS(); + if (argc < 2 || argc > 3 || zend_get_parameters_ex(argc, &a_arg, &b_arg, &round_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + switch (argc) { + case 3: + convert_to_long_ex(round_arg); + round = Z_LVAL_PP(round_arg); + break; + case 2: + round = GMP_ROUND_ZERO; + break; + } + + switch(round) { + case GMP_ROUND_ZERO: + gmp_zval_binary_ui_op_ex(return_value, a_arg, b_arg, mpz_tdiv_r, (gmp_binary_ui_op_t)mpz_tdiv_r_ui,1); + break; + case GMP_ROUND_PLUSINF: + gmp_zval_binary_ui_op_ex(return_value, a_arg, b_arg, mpz_cdiv_r, (gmp_binary_ui_op_t)mpz_cdiv_r_ui,1); + break; + case GMP_ROUND_MINUSINF: + gmp_zval_binary_ui_op_ex(return_value, a_arg, b_arg, mpz_fdiv_r, (gmp_binary_ui_op_t)mpz_fdiv_r_ui,1); + break; + } +} +/* }}} */ + +/* {{{ proto array gmp_div_q(resource a, resource b [, int round]) + Divide a by b, return quotient only + */ +ZEND_FUNCTION(gmp_div_q) +{ + zval **a_arg, **b_arg, **round_arg; + int round, argc; + + argc = ZEND_NUM_ARGS(); + if (argc < 2 || argc > 3 || zend_get_parameters_ex(argc, &a_arg, &b_arg, &round_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + switch (argc) { + case 3: + convert_to_long_ex(round_arg); + round = Z_LVAL_PP(round_arg); + break; + case 2: + round = GMP_ROUND_ZERO; + break; + } + + switch(round) { + case GMP_ROUND_ZERO: + gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_tdiv_q, (gmp_binary_ui_op_t)mpz_tdiv_q_ui); + break; + case GMP_ROUND_PLUSINF: + gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_cdiv_q, (gmp_binary_ui_op_t)mpz_cdiv_q_ui); + break; + case GMP_ROUND_MINUSINF: + gmp_zval_binary_ui_op(return_value, a_arg, b_arg, mpz_fdiv_q, (gmp_binary_ui_op_t)mpz_fdiv_q_ui); + break; + } + +} +/* }}} */ + +/* {{{ proto resource gmp_mod(resource a, resource b) + Compute a modulo b + */ +ZEND_FUNCTION(gmp_mod) +{ + zval **a_arg, **b_arg; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + gmp_zval_binary_ui_op_ex(return_value,a_arg,b_arg,mpz_mod,(gmp_binary_ui_op_t)mpz_mod_ui,1); +} +/* }}} */ + +/* {{{ proto resource gmp_divexact(resource a, resource b) + Divide a by b using exact division algorithm + */ +ZEND_FUNCTION(gmp_divexact) +{ + gmp_binary_op(mpz_divexact); +} +/* }}} */ + +/* {{{ proto resource gmp_neg(resource a) + Negate a number + */ +ZEND_FUNCTION(gmp_neg) +{ + gmp_unary_op(mpz_neg); +} +/* }}} */ + +/* {{{ proto resource gmp_abs(resource a) + Calculate absolute value + */ +ZEND_FUNCTION(gmp_abs) +{ + gmp_unary_op(mpz_abs); +} +/* }}} */ + +/* {{{ proto resource gmp_fact(int a) + Calculata factorial function + */ +ZEND_FUNCTION(gmp_fact) +{ + gmp_unary_ui_op(mpz_fac_ui); +} +/* }}} */ + +/* {{{ proto resource gmp_powm(resource base, int exp) + Raise base to power exp + */ +ZEND_FUNCTION(gmp_pow) +{ + zval **base_arg, **exp_arg; + mpz_t *gmpnum_result, *gmpnum_base; + int use_ui=0; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &base_arg, &exp_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + if(Z_TYPE_PP(base_arg) == IS_LONG && Z_LVAL_PP(base_arg) >= 0) { + use_ui=1; + } else { + FETCH_GMP_ZVAL(gmpnum_base, base_arg); + } + + convert_to_long_ex(exp_arg); + + if(Z_LVAL_PP(exp_arg) < 0) { + zend_error(E_WARNING,"Negative exponent not supported"); + RETURN_FALSE; + } + + INIT_GMP_NUM(gmpnum_result); + if(use_ui) { + mpz_ui_pow_ui(*gmpnum_result, Z_LVAL_PP(base_arg), Z_LVAL_PP(exp_arg)); + } else { + mpz_pow_ui(*gmpnum_result, *gmpnum_base, Z_LVAL_PP(exp_arg)); + } + + ZEND_REGISTER_RESOURCE(return_value, gmpnum_result, le_gmp); +} +/* }}} */ + +/* {{{ proto resource gmp_powm(resource base, resource exp, resource mod) + Raise base to power exp and take result modulo mod + */ +ZEND_FUNCTION(gmp_powm) +{ + zval **base_arg, **exp_arg, **mod_arg; + mpz_t *gmpnum_base, *gmpnum_exp, *gmpnum_mod, *gmpnum_result; + int use_ui=0; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &base_arg, &exp_arg, &mod_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_base, base_arg); + if(Z_TYPE_PP(exp_arg) == IS_LONG && Z_LVAL_PP(exp_arg) >= 0) { + use_ui=1; + } else { + FETCH_GMP_ZVAL(gmpnum_exp, exp_arg); + } + FETCH_GMP_ZVAL(gmpnum_mod, mod_arg); + + INIT_GMP_NUM(gmpnum_result); + if(use_ui) { + mpz_powm_ui(*gmpnum_result, *gmpnum_base, (unsigned long)Z_LVAL_PP(exp_arg), *gmpnum_mod); + } else { + mpz_powm(*gmpnum_result, *gmpnum_base, *gmpnum_exp, *gmpnum_mod); + } + + ZEND_REGISTER_RESOURCE(return_value, gmpnum_result, le_gmp); + +} +/* }}} */ + +/* {{{ proto resource gmp_sqrt(resource a) + Take integer part of square root of a + */ +ZEND_FUNCTION(gmp_sqrt) +{ + gmp_unary_op(mpz_sqrt); +} +/* }}} */ + +/* {{{ proto array gmp_sqrtrem(resource a) + Take integer part of square root of a + */ +ZEND_FUNCTION(gmp_sqrtrem) +{ + zval **a_arg; + mpz_t *gmpnum_a, *gmpnum_result1, *gmpnum_result2; + zval r; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &a_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + + INIT_GMP_NUM(gmpnum_result1); + INIT_GMP_NUM(gmpnum_result2); + + mpz_sqrtrem(*gmpnum_result1, *gmpnum_result2, *gmpnum_a); + + array_init(return_value); + ZEND_REGISTER_RESOURCE(&r, gmpnum_result1, le_gmp); + add_index_resource(return_value, 0, Z_LVAL(r)); + ZEND_REGISTER_RESOURCE(&r, gmpnum_result2, le_gmp); + add_index_resource(return_value, 1, Z_LVAL(r)); +} +/* }}} */ + +/* {{{ proto bool gmp_perfect_square(resource a) + Check if a is an exact square + */ +ZEND_FUNCTION(gmp_perfect_square) +{ + zval **a_arg; + mpz_t *gmpnum_a; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &a_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + + RETURN_BOOL(mpz_perfect_square_p(*gmpnum_a)); +} +/* }}} */ + +/* {{{ proto bool gmp_prob_prime(resource a[, int reps]) + Check if a is a "probably prime" + */ +ZEND_FUNCTION(gmp_prob_prime) +{ + zval **gmpnumber_arg, **reps_arg; + mpz_t *gmpnum_a; + int argc, reps; + + argc = ZEND_NUM_ARGS(); + if (argc < 1 || argc > 2 || zend_get_parameters_ex(argc, &gmpnumber_arg, &reps_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, gmpnumber_arg); + + switch (argc) { + case 2: + convert_to_long_ex(reps_arg); + reps = Z_LVAL_PP(reps_arg); + break; + case 1: + reps = 25; + break; + } + + RETURN_BOOL(mpz_probab_prime_p(*gmpnum_a, reps)); +} +/* }}} */ + +/* {{{ proto resource gmp_gcd(resource a, resource b) + Compute greatest common denominator (gcd) of a and b + */ +ZEND_FUNCTION(gmp_gcd) +{ + zval **a_arg, **b_arg; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + gmp_zval_binary_ui_op_ex(return_value,a_arg,b_arg,mpz_gcd,(gmp_binary_ui_op_t)mpz_gcd_ui,1); +} +/* }}} */ + +/* {{{ proto array gmp_gcdext(resource a, resource b) + Compute G, S, and T, such that AS + BT = G = `gcd' (A, B) + */ +ZEND_FUNCTION(gmp_gcdext) +{ + zval **a_arg, **b_arg; + mpz_t *gmpnum_a, *gmpnum_b, *gmpnum_t, *gmpnum_s, *gmpnum_g; + zval r; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + FETCH_GMP_ZVAL(gmpnum_b, a_arg); + + INIT_GMP_NUM(gmpnum_g); + INIT_GMP_NUM(gmpnum_s); + INIT_GMP_NUM(gmpnum_t); + + mpz_gcdext(*gmpnum_g, *gmpnum_s, *gmpnum_t, *gmpnum_a, *gmpnum_b); + + array_init(return_value); + + ZEND_REGISTER_RESOURCE(&r, gmpnum_g, le_gmp); + add_assoc_resource(return_value, "g", Z_LVAL(r)); + ZEND_REGISTER_RESOURCE(&r, gmpnum_s, le_gmp); + add_assoc_resource(return_value, "s", Z_LVAL(r)); + ZEND_REGISTER_RESOURCE(&r, gmpnum_t, le_gmp); + add_assoc_resource(return_value, "t", Z_LVAL(r)); +} +/* }}} */ + + +/* {{{ proto resource gmp_invert(resource a, resource b) + Compute the inverse of a modulo b + */ +ZEND_FUNCTION(gmp_invert) +{ + zval **a_arg, **b_arg; + mpz_t *gmpnum_a, *gmpnum_b, *gmpnum_result; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + FETCH_GMP_ZVAL(gmpnum_b, a_arg); + + INIT_GMP_NUM(gmpnum_result); + if(mpz_invert(*gmpnum_result,*gmpnum_a, *gmpnum_b)) { + ZEND_REGISTER_RESOURCE(return_value, gmpnum_result, le_gmp); + } else { + FREE_GMP_NUM(gmpnum_result); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto resource gmp_jacobi(resource a, resource b) + Compute Jacobi symbol + */ +ZEND_FUNCTION(gmp_jacobi) +{ + zval **a_arg, **b_arg; + mpz_t *gmpnum_a, *gmpnum_b; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + FETCH_GMP_ZVAL(gmpnum_b, a_arg); + + RETURN_LONG(mpz_jacobi(*gmpnum_a, *gmpnum_b)); +} +/* }}} */ + +/* {{{ proto resource gmp_legendre(resource a, resource b) + Compute Legendre symbol + */ +ZEND_FUNCTION(gmp_legendre) +{ + zval **a_arg, **b_arg; + mpz_t *gmpnum_a, *gmpnum_b; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + FETCH_GMP_ZVAL(gmpnum_b, a_arg); + + RETURN_LONG(mpz_legendre(*gmpnum_a, *gmpnum_b)); +} +/* }}} */ + +/* {{{ proto int gmp_cmp(resource a, resource b) + */ +ZEND_FUNCTION(gmp_cmp) +{ + zval **a_arg, **b_arg; + mpz_t *gmpnum_a, *gmpnum_b; + int use_si=0, res; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + + if(Z_TYPE_PP(b_arg) == IS_LONG) { + use_si=1; + } else { + FETCH_GMP_ZVAL(gmpnum_b, b_arg); + } + + if(use_si) { + res = mpz_cmp_si(*gmpnum_a, Z_LVAL_PP(b_arg)); + } else { + res = mpz_cmp(*gmpnum_a, *gmpnum_b); + } + + RETURN_LONG(res); +} +/* }}} */ + +/* {{{ proto int gmp_sign(resource a) + */ +ZEND_FUNCTION(gmp_sign) +{ + zval **a_arg; + mpz_t *gmpnum_a; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &a_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + + RETURN_LONG(mpz_sgn(*gmpnum_a)); +} +/* }}} */ + +/* {{{ proto resource gmp_random([int limiter]) + */ +ZEND_FUNCTION(gmp_random) +{ + zval **limiter_arg; + int limiter, argc; + mpz_t *gmpnum_result; + + argc = ZEND_NUM_ARGS(); + + if (argc < 0 || argc > 1 || zend_get_parameters_ex(1, &limiter_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + if(argc) { + convert_to_long_ex(limiter_arg); + limiter = Z_LVAL_PP(limiter_arg); + } else { + limiter = 20; + } + + INIT_GMP_NUM(gmpnum_result); + mpz_random(*gmpnum_result, limiter); + + ZEND_REGISTER_RESOURCE(return_value, gmpnum_result, le_gmp); +} +/* }}} */ + +/* {{{ proto resource gmp_and(resource a, resource b) + Calculate logical AND of a and b + */ +ZEND_FUNCTION(gmp_and) +{ + gmp_binary_op(mpz_and); +} +/* }}} */ + +/* {{{ proto resource gmp_or(resource a, resource b) + Calculate logical OR of a and b + */ +ZEND_FUNCTION(gmp_or) +{ + gmp_binary_op(mpz_ior); +} +/* }}} */ + +/* {{{ proto resource gmp_or(resource a) + Calculate one's complement of a + */ +ZEND_FUNCTION(gmp_com) +{ + gmp_unary_op(mpz_com); +} +/* }}} */ + +/* {{{ proto resource gmp_xor(resource a, resource b) + Calculate logical exclusive OR of a and b + */ +ZEND_FUNCTION(gmp_xor) +{ + /* use formula: a^b = (a|b)&^(a&b) */ + zval **a_arg, **b_arg; + mpz_t *gmpnum_a, *gmpnum_b, *gmpnum_result, *gmpnum_t; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &b_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + FETCH_GMP_ZVAL(gmpnum_a, a_arg); + FETCH_GMP_ZVAL(gmpnum_b, b_arg); + + INIT_GMP_NUM(gmpnum_result); + INIT_GMP_NUM(gmpnum_t); + + mpz_and(*gmpnum_t, *gmpnum_a, *gmpnum_b); + mpz_com(*gmpnum_t, *gmpnum_t); + + mpz_ior(*gmpnum_result, *gmpnum_a, *gmpnum_b); + mpz_and(*gmpnum_result, *gmpnum_result, *gmpnum_t); + + FREE_GMP_NUM(gmpnum_t); + + ZEND_REGISTER_RESOURCE(return_value, gmpnum_result, le_gmp); +} +/* }}} */ + +/* {{{ proto void gmp_setbit(resource &a, int index[, bool set_clear]) + Set or clear bit in a + */ +ZEND_FUNCTION(gmp_setbit) +{ + zval **a_arg, **ind_arg, **sc_arg; + int argc, index, set; + mpz_t *gmpnum_a; + + argc = ZEND_NUM_ARGS(); + if (argc < 2 || argc > 3 || zend_get_parameters_ex(argc, &a_arg, &ind_arg, &sc_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(gmpnum_a, mpz_t *, a_arg, -1, GMP_RESOURCE_NAME, le_gmp); + + convert_to_long_ex(ind_arg); + index = Z_LVAL_PP(ind_arg); + + switch (argc) { + case 3: + convert_to_long_ex(sc_arg); + set = Z_LVAL_PP(sc_arg); + break; + case 2: + set = 1; + break; + } + + if(set) { + mpz_setbit(*gmpnum_a, index); + } else { + mpz_clrbit(*gmpnum_a, index); + } +} +/* }}} */ + +/* {{{ proto void gmp_clrbit(resource &a, int index) + Clear bit in a + */ +ZEND_FUNCTION(gmp_clrbit) +{ + zval **a_arg, **ind_arg; + int index; + mpz_t *gmpnum_a; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &a_arg, &ind_arg) == FAILURE){ + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(gmpnum_a, mpz_t *, a_arg, -1, GMP_RESOURCE_NAME, le_gmp); + + convert_to_long_ex(ind_arg); + index = Z_LVAL_PP(ind_arg); + + mpz_clrbit(*gmpnum_a, index); +} +/* }}} */ + +static void _php_gmpnum_free(zend_rsrc_list_entry *rsrc) +{ + mpz_t *gmpnum = (mpz_t *)rsrc->ptr; + FREE_GMP_NUM(gmpnum); +} + +#endif /* HAVE_GMP */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/gmp/php_gmp.h b/ext/gmp/php_gmp.h new file mode 100644 index 0000000000..519a70757b --- /dev/null +++ b/ext/gmp/php_gmp.h @@ -0,0 +1,113 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | 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. | + +----------------------------------------------------------------------+ + | Authors: Stanislav Malyshev <stas@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifndef PHP_GMP_H +#define PHP_GMP_H + +/* You should tweak config.m4 so this symbol (or some else suitable) + gets defined. +*/ +#if HAVE_GMP + +extern zend_module_entry gmp_module_entry; +#define phpext_gmp_ptr &gmp_module_entry + +#ifdef ZEND_WIN32 +#define GMP_API __declspec(dllexport) +#else +#define GMP_API +#endif + +ZEND_MINIT_FUNCTION(gmp); +ZEND_MSHUTDOWN_FUNCTION(gmp); +ZEND_MINFO_FUNCTION(gmp); + +ZEND_FUNCTION(gmp_init); +ZEND_FUNCTION(gmp_intval); +ZEND_FUNCTION(gmp_strval); +ZEND_FUNCTION(gmp_add); +ZEND_FUNCTION(gmp_sub); +ZEND_FUNCTION(gmp_mul); +ZEND_FUNCTION(gmp_div_qr); +ZEND_FUNCTION(gmp_div_q); +ZEND_FUNCTION(gmp_div_r); +ZEND_FUNCTION(gmp_mod); +ZEND_FUNCTION(gmp_divexact); +ZEND_FUNCTION(gmp_neg); +ZEND_FUNCTION(gmp_abs); +ZEND_FUNCTION(gmp_fact); +ZEND_FUNCTION(gmp_sqrt); +ZEND_FUNCTION(gmp_pow); +ZEND_FUNCTION(gmp_powm); +ZEND_FUNCTION(gmp_sqrtrem); +ZEND_FUNCTION(gmp_perfect_square); +ZEND_FUNCTION(gmp_prob_prime); +ZEND_FUNCTION(gmp_gcd); +ZEND_FUNCTION(gmp_gcdext); +ZEND_FUNCTION(gmp_invert); +ZEND_FUNCTION(gmp_jacobi); +ZEND_FUNCTION(gmp_legendre); +ZEND_FUNCTION(gmp_cmp); +ZEND_FUNCTION(gmp_sign); +ZEND_FUNCTION(gmp_and); +ZEND_FUNCTION(gmp_or); +ZEND_FUNCTION(gmp_com); +ZEND_FUNCTION(gmp_xor); +ZEND_FUNCTION(gmp_random); +ZEND_FUNCTION(gmp_setbit); +ZEND_FUNCTION(gmp_clrbit); + +/* + Declare any global variables you may need between the BEGIN + and END macros here: + +ZEND_BEGIN_MODULE_GLOBALS(gmp) + int global_variable; +ZEND_END_MODULE_GLOBALS(gmp) +*/ + +/* In every function that needs to use variables in php_gmp_globals, + do call GMPLS_FETCH(); after declaring other variables used by + that function, and always refer to them as GMPG(variable). + You are encouraged to rename these macros something shorter, see + examples in any other php module directory. +*/ + +#ifdef ZTS +#define GMPG(v) (gmp_globals->v) +#define GMPLS_FETCH() php_gmp_globals *gmp_globals = ts_resource(gmp_globals_id) +#else +#define GMPG(v) (gmp_globals.v) +#define GMPLS_FETCH() +#endif + +#else + +#define phpext_gmp_ptr NULL + +#endif + +#endif /* PHP_GMP_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ |