[php-src] master: ext/gmp: Add gmp_prevprime() (#22807)
Weilin Du via GitHub <[email protected]> Tue, 28 Jul 2026 08:38:21 +0000
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Weilin Du (LamentXU123)
Committer: GitHub (web-flow)
Pusher: LamentXU123
Date: 2026-07-28T16:38:19+08:00
Commit: https://github.com/php/php-src/commit/7abd4931d67a64fd76fd111b9f6ac885b9b018c5
Raw diff: https://github.com/php/php-src/commit/7abd4931d67a64fd76fd111b9f6ac885b9b018c5.diff
ext/gmp: Add gmp_prevprime() (#22807)
Expose mpz_prevprime() to the userland, namely gmp_prevprime().
Changed paths:
A ext/gmp/tests/gmp_prevprime.phpt
M NEWS
M UPGRADING
M ext/gmp/config.m4
M ext/gmp/config.w32
M ext/gmp/gmp.c
M ext/gmp/gmp.stub.php
M ext/gmp/gmp_arginfo.h
M ext/gmp/tests/bug80560.phpt
Diff:
diff --git a/NEWS b/NEWS
index 7ec4eb4b809e..a7aa29cd9043 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,7 @@ PHP NEWS
declares a default value for the attribute). (iliaal)
- 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 d30a5663274f..3d9cfdc6aeb9 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -271,6 +271,12 @@ 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. This function is
+ available only when PHP is built against GNU MP 6.3.0 or later; it is not
+ available on official Windows builds using MPIR.
+
- Intl:
. Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(),
with the alias of locale_get_display_keyword() and
@@ -421,6 +427,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 7f8799a7ae2d..9ade8eade215 100644
--- a/ext/gmp/gmp.c
+++ b/ext/gmp/gmp.c
@@ -1082,6 +1082,34 @@ 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
+ */
+ 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/gmp_prevprime.phpt b/ext/gmp/tests/gmp_prevprime.phpt
new file mode 100644
index 000000000000..42c08566c413
--- /dev/null
+++ b/ext/gmp/tests/gmp_prevprime.phpt
@@ -0,0 +1,34 @@
+--TEST--
+gmp_prevprime()
+--EXTENSIONS--
+gmp
+--SKIPIF--
+<?php
+if (!function_exists('gmp_prevprime')) {
+ die('skip gmp_prevprime() is not available');
+}
+?>
+--FILE--
+<?php
+
+foreach ([-1, 0, 1, 2] as $value) {
+ try {
+ var_dump(gmp_prevprime($value));
+ } catch (\ValueError $e) {
+ echo $e->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"