@@ -2065,3 +2065,194 @@ def test_radians(op, degrees, expected_radians):
20652065    else :
20662066        np .testing .assert_allclose (float (result ), expected_radians , rtol = 1e-13 )
20672067
2068+ 
2069+ class  TestNextAfter :
2070+     """Test cases for np.nextafter function with QuadPrecision dtype""" 
2071+     
2072+     @pytest .mark .parametrize ("x1,x2" , [ 
2073+         # NaN tests  
2074+         (np .nan , 1.0 ), 
2075+         (1.0 , np .nan ), 
2076+         (np .nan , np .nan ), 
2077+     ]) 
2078+     def  test_nan (self , x1 , x2 ):
2079+         """Test nextafter with NaN inputs returns NaN""" 
2080+         q_x1  =  QuadPrecision (x1 )
2081+         q_x2  =  QuadPrecision (x2 )
2082+         
2083+         result  =  np .nextafter (q_x1 , q_x2 )
2084+         
2085+         assert  isinstance (result , QuadPrecision )
2086+         assert  np .isnan (float (result ))
2087+ 
2088+     def  test_precision (self ):
2089+         """Test that nextafter provides the exact next representable value""" 
2090+         # Start with 1.0 and move towards 2.0 
2091+         x1  =  QuadPrecision (1.0 )
2092+         x2  =  QuadPrecision (2.0 )
2093+         
2094+         result  =  np .nextafter (x1 , x2 )
2095+         
2096+         # Get machine epsilon from finfo 
2097+         finfo  =  np .finfo (QuadPrecDType ())
2098+         expected  =  x1  +  finfo .eps 
2099+         
2100+         # result should be exactly 1.0 + eps 
2101+         assert  result  ==  expected 
2102+         
2103+         # Moving the other direction should give us back 1.0 
2104+         result_back  =  np .nextafter (result , x1 )
2105+         assert  result_back  ==  x1 
2106+ 
2107+     def  test_smallest_subnormal (self ):
2108+         """Test that nextafter(0.0, 1.0) returns the smallest positive subnormal (TRUE_MIN)""" 
2109+         zero  =  QuadPrecision (0.0 )
2110+         one  =  QuadPrecision (1.0 )
2111+ 
2112+         result  =  np .nextafter (zero , one )  # smallest_subnormal 
2113+         finfo  =  np .finfo (QuadPrecDType ())
2114+         
2115+         assert  result  ==  finfo .smallest_subnormal , \
2116+             f"nextafter(0.0, 1.0) should equal smallest_subnormal, got { result } { finfo .smallest_subnormal }  
2117+         
2118+         # Verify it's positive and very small 
2119+         assert  result  >  zero , "nextafter(0.0, 1.0) should be positive" 
2120+         
2121+         # Moving back towards zero should give us zero 
2122+         result_back  =  np .nextafter (result , zero )
2123+         assert  result_back  ==  zero , f"nextafter(smallest_subnormal, 0.0) should be 0.0, got { result_back }  
2124+ 
2125+     def  test_negative_zero (self ):
2126+         """Test nextafter with negative zero""" 
2127+         neg_zero  =  QuadPrecision (- 0.0 )
2128+         pos_zero  =  QuadPrecision (0.0 )
2129+         one  =  QuadPrecision (1.0 )
2130+         neg_one  =  QuadPrecision (- 1.0 )
2131+         
2132+         finfo  =  np .finfo (QuadPrecDType ())
2133+         
2134+         # nextafter(-0.0, 1.0) should return smallest positive subnormal 
2135+         result  =  np .nextafter (neg_zero , one )
2136+         assert  result  ==  finfo .smallest_subnormal , \
2137+             f"nextafter(-0.0, 1.0) should be smallest_subnormal, got { result }  
2138+         assert  result  >  pos_zero , "Result should be positive" 
2139+         
2140+         # nextafter(+0.0, -1.0) should return smallest negative subnormal 
2141+         result_neg  =  np .nextafter (pos_zero , neg_one )
2142+         expected_neg_subnormal  =  - finfo .smallest_subnormal 
2143+         assert  result_neg  ==  expected_neg_subnormal , \
2144+             f"nextafter(+0.0, -1.0) should be -smallest_subnormal, got { result_neg }  
2145+         assert  result_neg  <  pos_zero , "Result should be negative" 
2146+ 
2147+     def  test_infinity_cases (self ):
2148+         """Test nextafter with infinity edge cases""" 
2149+         pos_inf  =  QuadPrecision (np .inf )
2150+         neg_inf  =  QuadPrecision (- np .inf )
2151+         one  =  QuadPrecision (1.0 )
2152+         neg_one  =  QuadPrecision (- 1.0 )
2153+         zero  =  QuadPrecision (0.0 )
2154+         
2155+         finfo  =  np .finfo (QuadPrecDType ())
2156+         
2157+         # nextafter(+inf, finite) should return max finite value 
2158+         result  =  np .nextafter (pos_inf , zero )
2159+         assert  not  np .isinf (result ), "nextafter(+inf, 0) should be finite" 
2160+         assert  result  <  pos_inf , "Result should be less than +inf" 
2161+         assert  result  ==  finfo .max , f"nextafter(+inf, 0) should be max, got { result } { finfo .max }  
2162+         
2163+         # nextafter(-inf, finite) should return -max (most negative finite) 
2164+         result_neg  =  np .nextafter (neg_inf , zero )
2165+         assert  not  np .isinf (result_neg ), "nextafter(-inf, 0) should be finite" 
2166+         assert  result_neg  >  neg_inf , "Result should be greater than -inf" 
2167+         assert  result_neg  ==  - finfo .max , f"nextafter(-inf, 0) should be -max, got { result_neg }  
2168+         
2169+         # Verify symmetry: nextafter(result, +inf) should give us +inf back 
2170+         back_to_inf  =  np .nextafter (result , pos_inf )
2171+         assert  back_to_inf  ==  pos_inf , "nextafter(max_finite, +inf) should be +inf" 
2172+         
2173+         # nextafter(+inf, +inf) should return +inf 
2174+         result_inf  =  np .nextafter (pos_inf , pos_inf )
2175+         assert  result_inf  ==  pos_inf , "nextafter(+inf, +inf) should be +inf" 
2176+         
2177+         # nextafter(-inf, -inf) should return -inf 
2178+         result_neg_inf  =  np .nextafter (neg_inf , neg_inf )
2179+         assert  result_neg_inf  ==  neg_inf , "nextafter(-inf, -inf) should be -inf" 
2180+ 
2181+     def  test_max_to_infinity (self ):
2182+         """Test nextafter from max finite value to infinity""" 
2183+         finfo  =  np .finfo (QuadPrecDType ())
2184+         max_val  =  finfo .max 
2185+         pos_inf  =  QuadPrecision (np .inf )
2186+         neg_inf  =  QuadPrecision (- np .inf )
2187+         
2188+         # nextafter(max_finite, +inf) should return +inf 
2189+         result  =  np .nextafter (max_val , pos_inf )
2190+         assert  np .isinf (result ), f"nextafter(max, +inf) should be inf, got { result }  
2191+         assert  result  >  max_val , "Result should be greater than max" 
2192+         assert  result  ==  pos_inf , "Result should be +inf" 
2193+         
2194+         # nextafter(-max_finite, -inf) should return -inf 
2195+         neg_max_val  =  - max_val 
2196+         result_neg  =  np .nextafter (neg_max_val , neg_inf )
2197+         assert  np .isinf (result_neg ), f"nextafter(-max, -inf) should be -inf, got { result_neg }  
2198+         assert  result_neg  <  neg_max_val , "Result should be less than -max" 
2199+         assert  result_neg  ==  neg_inf , "Result should be -inf" 
2200+ 
2201+     def  test_near_max (self ):
2202+         """Test nextafter near maximum finite value""" 
2203+         finfo  =  np .finfo (QuadPrecDType ())
2204+         max_val  =  finfo .max 
2205+         zero  =  QuadPrecision (0.0 )
2206+         pos_inf  =  QuadPrecision (np .inf )
2207+         
2208+         # nextafter(max, 0) should return a value less than max 
2209+         result  =  np .nextafter (max_val , zero )
2210+         assert  result  <  max_val , "nextafter(max, 0) should be less than max" 
2211+         assert  not  np .isinf (result ), "Result should be finite" 
2212+         
2213+         # The difference should be one ULP at that scale 
2214+         # Moving back should give us max again 
2215+         result_back  =  np .nextafter (result , pos_inf )
2216+         assert  result_back  ==  max_val , f"Moving back should return max, got { result_back }  
2217+ 
2218+     def  test_symmetry (self ):
2219+         """Test symmetry properties of nextafter""" 
2220+         values  =  [0.0 , 1.0 , - 1.0 , 1e10 , - 1e10 , 1e-10 , - 1e-10 ]
2221+         
2222+         for  val  in  values :
2223+             q_val  =  QuadPrecision (val )
2224+             
2225+             # nextafter(x, +direction) then nextafter(result, x) should return x 
2226+             if  not  np .isinf (val ):
2227+                 result_up  =  np .nextafter (q_val , QuadPrecision (np .inf ))
2228+                 result_back  =  np .nextafter (result_up , q_val )
2229+                 assert  result_back  ==  q_val , \
2230+                     f"Symmetry failed for { val }  
2231+                 
2232+                 # Same for down direction 
2233+                 result_down  =  np .nextafter (q_val , QuadPrecision (- np .inf ))
2234+                 result_back_down  =  np .nextafter (result_down , q_val )
2235+                 assert  result_back_down  ==  q_val , \
2236+                     f"Symmetry failed for { val }  
2237+ 
2238+     def  test_direction (self ):
2239+         """Test that nextafter moves in the correct direction""" 
2240+         test_cases  =  [
2241+             (1.0 , 2.0 , "greater" ),    # towards larger 
2242+             (2.0 , 1.0 , "less" ),       # towards smaller 
2243+             (- 1.0 , - 2.0 , "less" ),     # towards more negative 
2244+             (- 2.0 , - 1.0 , "greater" ),  # towards less negative 
2245+             (1.0 , np .inf , "greater" ), # towards +inf 
2246+             (1.0 , - np .inf , "less" ),   # towards -inf 
2247+         ]
2248+         
2249+         for  x , y , expected_dir  in  test_cases :
2250+             q_x  =  QuadPrecision (x )
2251+             q_y  =  QuadPrecision (y )
2252+             result  =  np .nextafter (q_x , q_y )
2253+             
2254+             if  expected_dir  ==  "greater" :
2255+                 assert  result  >  q_x , f"nextafter({ x } { y } { x } { result }  
2256+             elif  expected_dir  ==  "less" :
2257+                 assert  result  <  q_x , f"nextafter({ x } { y } { x } { result }  
2258+ 
0 commit comments