diff --git a/NEWS b/NEWS index 0ad622eec829..99a3b2089e8f 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ PHP NEWS . Implemented partial function application RFC. (Arnaud) - GMP: + . Added gmp_prevprime(). (Weilin Du, David Carlier) . Fixed GMP power and shift operators to reject GMP right operands outside the unsigned long range instead of silently truncating them. (Weilin Du) . Fixed GMP integer string parsing to reject strings containing NUL bytes diff --git a/UPGRADING b/UPGRADING index 1b7cda4b8612..0d929f57a4fb 100644 --- a/UPGRADING +++ b/UPGRADING @@ -260,6 +260,11 @@ PHP 8.6 UPGRADE NOTES - Fileinfo: . finfo_file() now works with remote streams. +- GMP: + . Added gmp_prevprime() to get the largest prime smaller than the given + number. A ValueError is thrown if no such prime exists. Requires GNU MP + 6.3.0 or later. + - Intl: . Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(), with the alias of locale_get_display_keyword() and @@ -401,6 +406,9 @@ PHP 8.6 UPGRADE NOTES 6. New Functions ======================================== +- GMP: + . gmp_prevprime() + - Intl: . grapheme_strrev() RFC: https://wiki.php.net/rfc/grapheme_strrev diff --git a/ext/gmp/config.m4 b/ext/gmp/config.m4 index f0f07d377078..c4ffc2d42d4d 100644 --- a/ext/gmp/config.m4 +++ b/ext/gmp/config.m4 @@ -22,6 +22,7 @@ if test "$PHP_GMP" != "no"; then LIBS="$LIBS $GMP_LIBS" gmp_check=no AC_CHECK_HEADER([gmp.h], [AC_CHECK_FUNC([__gmpz_rootrem], [gmp_check=yes])]) + AC_CHECK_FUNCS([__gmpz_prevprime]) CFLAGS=$CFLAGS_SAVED LIBS=$LIBS_SAVED diff --git a/ext/gmp/config.w32 b/ext/gmp/config.w32 index dc0c1e978d31..d83048ebe9c6 100644 --- a/ext/gmp/config.w32 +++ b/ext/gmp/config.w32 @@ -5,6 +5,9 @@ ARG_WITH("gmp", "Include GNU MP support.", "no"); if (PHP_GMP != "no") { if (CHECK_LIB("mpir_a.lib", "gmp", PHP_GMP) && CHECK_HEADER("gmp.h", "CFLAGS_GMP", PHP_GMP + ";" + PHP_PHP_BUILD + "\\include\\mpir")) { + if (GREP_HEADER("gmp.h", "mpz_prevprime", PHP_GMP + ";" + PHP_PHP_BUILD + "\\include\\mpir")) { + AC_DEFINE('HAVE___GMPZ_PREVPRIME', 1, "Define to 1 if GMP has the 'mpz_prevprime' function."); + } EXTENSION("gmp", "gmp.c", null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); PHP_INSTALL_HEADERS("ext/gmp", "php_gmp_int.h"); AC_DEFINE('HAVE_GMP', 1, "Define to 1 if the PHP extension 'gmp' is available."); diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index a5cbdba3484e..a5bd42b31305 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -1082,6 +1082,37 @@ GMP_UNARY_OP_FUNCTION(com); /* {{{ Finds next prime of a */ GMP_UNARY_OP_FUNCTION(nextprime); +#ifdef HAVE___GMPZ_PREVPRIME +/* {{{ Finds previous prime of a */ +ZEND_FUNCTION(gmp_prevprime) +{ + mpz_ptr gmpnum_a, gmpnum_result; + int res; + + ZEND_PARSE_PARAMETERS_START(1, 1) + GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a) + ZEND_PARSE_PARAMETERS_END(); + + if (mpz_cmp_ui(gmpnum_a, 2) <= 0) { + /* + * mpz_prevprime() returns 0 when no previous prime exists, which happens + * for operands not greater than 2. + * https://gmplib.org/manual/Number-Theoretic-Functions#index-mpz_005fprevprime + * However. since returning a prime number smaller than 2 is mathemeticaly + * impossible, throwing a ValueError instead is a more widely used solution + * throughout the php code base. + */ + zend_argument_value_error(1, "must be greater than 2"); + RETURN_THROWS(); + } + + INIT_GMP_RETVAL(gmpnum_result); + res = mpz_prevprime(gmpnum_result, gmpnum_a); + ZEND_ASSERT(res); +} +/* }}} */ +#endif + /* Add a and b */ GMP_BINARY_OP_FUNCTION(add); /* Subtract b from a */ diff --git a/ext/gmp/gmp.stub.php b/ext/gmp/gmp.stub.php index 75812c62c5ca..d3c603310a4a 100644 --- a/ext/gmp/gmp.stub.php +++ b/ext/gmp/gmp.stub.php @@ -183,4 +183,8 @@ function gmp_hamdist(GMP|int|string $num1, GMP|int|string $num2): int {} function gmp_nextprime(GMP|int|string $num): GMP {} +#ifdef HAVE___GMPZ_PREVPRIME +function gmp_prevprime(GMP|int|string $num): GMP {} +#endif + function gmp_binomial(GMP|int|string $n, int $k): GMP {} diff --git a/ext/gmp/gmp_arginfo.h b/ext/gmp/gmp_arginfo.h index 436e3a22ea72..957fdc8d7765 100644 --- a/ext/gmp/gmp_arginfo.h +++ b/ext/gmp/gmp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit gmp.stub.php instead. - * Stub hash: 3aabd5a5d2db0df15b249a425465ae718c13ab6b */ + * Stub hash: 9d651cc4ba238a496ebe8302fe3e0f985d48d769 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_init, 0, 1, GMP, 0) ZEND_ARG_TYPE_MASK(0, num, MAY_BE_LONG|MAY_BE_STRING, NULL) @@ -179,6 +179,12 @@ ZEND_END_ARG_INFO() #define arginfo_gmp_nextprime arginfo_gmp_neg +#if defined(HAVE___GMPZ_PREVPRIME) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_prevprime, 0, 1, GMP, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, num, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_binomial, 0, 2, GMP, 0) ZEND_ARG_OBJ_TYPE_MASK(0, n, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO(0, k, IS_LONG, 0) @@ -245,6 +251,9 @@ ZEND_FUNCTION(gmp_scan1); ZEND_FUNCTION(gmp_popcount); ZEND_FUNCTION(gmp_hamdist); ZEND_FUNCTION(gmp_nextprime); +#if defined(HAVE___GMPZ_PREVPRIME) +ZEND_FUNCTION(gmp_prevprime); +#endif ZEND_FUNCTION(gmp_binomial); ZEND_METHOD(GMP, __construct); ZEND_METHOD(GMP, __serialize); @@ -301,6 +310,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(gmp_popcount, arginfo_gmp_popcount) ZEND_FE(gmp_hamdist, arginfo_gmp_hamdist) ZEND_FE(gmp_nextprime, arginfo_gmp_nextprime) +#if defined(HAVE___GMPZ_PREVPRIME) + ZEND_FE(gmp_prevprime, arginfo_gmp_prevprime) +#endif ZEND_FE(gmp_binomial, arginfo_gmp_binomial) ZEND_FE_END }; diff --git a/ext/gmp/tests/bug80560.phpt b/ext/gmp/tests/bug80560.phpt index 25af4a42d9b9..c5ccc050070c 100644 --- a/ext/gmp/tests/bug80560.phpt +++ b/ext/gmp/tests/bug80560.phpt @@ -24,6 +24,9 @@ $functions1 = [ 'gmp_com', 'gmp_nextprime', ]; +if (function_exists('gmp_prevprime')) { + $functions1[] = 'gmp_prevprime'; +} $functions1_need_int_2 = [ 'gmp_testbit', 'gmp_scan0', diff --git a/ext/gmp/tests/gh13661.phpt b/ext/gmp/tests/gh13661.phpt new file mode 100644 index 000000000000..42c08566c413 --- /dev/null +++ b/ext/gmp/tests/gh13661.phpt @@ -0,0 +1,34 @@ +--TEST-- +gmp_prevprime() +--EXTENSIONS-- +gmp +--SKIPIF-- + +--FILE-- +getMessage() . \PHP_EOL; + } +} + +var_dump(gmp_strval(gmp_prevprime(3))); +var_dump(gmp_strval(gmp_prevprime(4))); +var_dump(gmp_strval(gmp_prevprime(10000))); + +?> +--EXPECT-- +gmp_prevprime(): Argument #1 ($num) must be greater than 2 +gmp_prevprime(): Argument #1 ($num) must be greater than 2 +gmp_prevprime(): Argument #1 ($num) must be greater than 2 +gmp_prevprime(): Argument #1 ($num) must be greater than 2 +string(1) "2" +string(1) "3" +string(4) "9973"