Repository: numba/numba

Issue:
Support math.nextafter in nopython mode
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

-->

## Feature request

<!--

Please include details of the feature you would like to see, why you would
like to see it/the use case.

-->
`math.nextafter` was added in Python 3.9. However, numba doesn't seem to support it in nopython mode. Can support please be added?


Discussion:
If anyone wants to work on this one, I think one can reuse the same implementation of `np.nextafter` to implement this one:
https://github.com/numba/numba/blob/3edb458c69daeaaffbfc938145c367eda1cf043f/numba/np/npyfuncs.py#L1637-L1646
I'll try taking a crack at it.

Resolving patch:
diff --git a/numba/core/typing/mathdecl.py b/numba/core/typing/mathdecl.py
--- a/numba/core/typing/mathdecl.py
+++ b/numba/core/typing/mathdecl.py
@@ -86,6 +86,14 @@ class Math_hypot(ConcreteTemplate):
     ]
 
 
+@infer_global(math.nextafter)
+class Math_nextafter(ConcreteTemplate):
+    cases = [
+        signature(types.float64, types.float64, types.float64),
+        signature(types.float32, types.float32, types.float32),
+    ]
+
+
 @infer_global(math.isinf)
 @infer_global(math.isnan)
 class Math_predicate(ConcreteTemplate):
diff --git a/numba/cpython/mathimpl.py b/numba/cpython/mathimpl.py
--- a/numba/cpython/mathimpl.py
+++ b/numba/cpython/mathimpl.py
@@ -406,6 +406,21 @@ def pow_impl(context, builder, sig, args):
 
 # -----------------------------------------------------------------------------
 
+@lower(math.nextafter, types.Float, types.Float)
+def nextafter_impl(context, builder, sig, args):
+    assert len(args) == 2
+    ty = sig.args[0]
+    lty = context.get_value_type(ty)
+    func_name = {
+        types.float32: "nextafterf",
+        types.float64: "nextafter"
+        }[ty]
+    fnty = llvmlite.ir.FunctionType(lty, (lty, lty))
+    fn = cgutils.insert_pure_function(builder.module, fnty, name=func_name)
+    res = builder.call(fn, args)
+    return impl_ret_untracked(context, builder, sig.return_type, res)
+
+# -----------------------------------------------------------------------------
 
 def _unsigned(T):
     """Convert integer to unsigned integer of equivalent width."""


Test patch:
diff --git a/numba/tests/test_mathlib.py b/numba/tests/test_mathlib.py
--- a/numba/tests/test_mathlib.py
+++ b/numba/tests/test_mathlib.py
@@ -119,6 +119,10 @@ def hypot(x, y):
     return math.hypot(x, y)
 
 
+def nextafter(x, y):
+    return math.nextafter(x, y)
+
+
 def degrees(x):
     return math.degrees(x)
 
@@ -428,6 +432,25 @@ def naive_hypot(x, y):
                                         'overflow encountered in .*scalar',
                                         naive_hypot, val, val)
 
+    def test_nextafter(self):
+        pyfunc = nextafter
+        x_types = [types.float32, types.float64,
+                   types.int32, types.int64,
+                   types.uint32, types.uint64]
+        x_values = [0.0, .21, .34, 1005382.042, -25.328]
+        y1_values = [x + 2 for x in x_values]
+        y2_values = [x - 2 for x in x_values]
+
+        self.run_binary(pyfunc, x_types, x_values, y1_values)
+        self.run_binary(pyfunc, x_types, x_values, y2_values)
+
+        # Test using pos/neg inf
+        self.run_binary(pyfunc, x_types, [0.0, -.5, .5], [math.inf]*3)
+        self.run_binary(pyfunc, x_types, [0.0, -.5, .5], [-math.inf]*3)
+
+        # if both args to nextafter are equal, then it is returned unchanged.
+        self.run_binary(pyfunc, x_types, x_values, x_values)
+
     def test_degrees(self):
         pyfunc = degrees
         x_types = [types.int16, types.int32, types.int64,