From 3e4f5b17d0add43ee086a69c026b2fcc9ebc94e7 Mon Sep 17 00:00:00 2001
From: Yuao Ma <c8ef@outlook.com>
Date: Mon, 12 May 2025 23:07:37 +0800
Subject: [PATCH] fortran: map atand(y, x) to atan2d(y, x) [PR113413]

According to the Fortran standard, atand(y, x) is equivalent to atan2d(y, x).
However, the current atand(y, x) function produces an error. This patch
includes the necessary intrinsic mapping, related test, and intrinsic
documentation.
The minor comment change in intrinsic.cc is cherry-picked from Steve's previous
work.

PR fortran/113413 - ATAND(Y,X) is unsupported

	PR fortran/113413

gcc/fortran/ChangeLog:

	* intrinsic.cc (do_check): Minor doc polish.
	(add_functions): Add atand(y, x) mapping.
	* intrinsic.texi: Update atand example.

gcc/testsuite/ChangeLog:

	* gfortran.dg/dec_math.f90: Add atand(y, x) testcase.

Signed-off-by: Yuao Ma <c8ef@outlook.com>
Co-authored-by: Steven G. Kargl <kargl@gcc.gnu.org>
---
 gcc/fortran/intrinsic.cc               | 19 +++++---
 gcc/fortran/intrinsic.texi             | 13 ++++--
 gcc/testsuite/gfortran.dg/dec_math.f90 | 63 ++++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 11 deletions(-)

diff --git a/gcc/fortran/intrinsic.cc b/gcc/fortran/intrinsic.cc
index 2eba2094606..908e1dae128 100644
--- a/gcc/fortran/intrinsic.cc
+++ b/gcc/fortran/intrinsic.cc
@@ -376,11 +376,11 @@ do_check (gfc_intrinsic_sym *specific, gfc_actual_arglist *arg)
 
    Argument list:
       char *     name of function
-      int	whether function is elemental
-      int	If the function can be used as an actual argument [1]
-      bt	 return type of function
-      int	kind of return type of function
-      int	Fortran standard version
+      int        whether function is elemental
+      int        If the function can be used as an actual argument [1]
+      bt         return type of function
+      int        kind of return type of function
+      int        Fortran standard version
       check      pointer to check function
       simplify   pointer to simplification function
       resolve    pointer to resolution function
@@ -396,7 +396,7 @@ do_check (gfc_intrinsic_sym *specific, gfc_actual_arglist *arg)
 
 
  [1] Whether a function can or cannot be used as an actual argument is
-     determined by its presence on the 13.6 list in Fortran 2003.  The
+     determined by its presence in the 13.6 list in Fortran 2003.  The
      following intrinsics, which are GNU extensions, are considered allowed
      as actual arguments: ACOSH ATANH DACOSH DASINH DATANH DCONJG DIMAG
      ZABS ZCOS ZEXP ZLOG ZSIN ZSQRT.  */
@@ -3479,6 +3479,13 @@ add_functions (void)
 	     gfc_check_fn_r, gfc_simplify_atand, gfc_resolve_trigd,
 	     x, BT_REAL, dr, REQUIRED);
 
+  /* Two-argument version of atand, equivalent to atan2d.  */
+  add_sym_2 ("atand", GFC_ISYM_ATAN2D, CLASS_ELEMENTAL, ACTUAL_YES,
+	     BT_REAL, dr, GFC_STD_F2023,
+	     gfc_check_atan2, gfc_simplify_atan2d, gfc_resolve_trigd2,
+	     y, BT_REAL, dr, REQUIRED,
+	     x, BT_REAL, dr, REQUIRED);
+
   make_generic ("atand", GFC_ISYM_ATAND, GFC_STD_F2023);
 
   add_sym_1 ("datand", GFC_ISYM_ATAND, CLASS_ELEMENTAL, ACTUAL_YES,
diff --git a/gcc/fortran/intrinsic.texi b/gcc/fortran/intrinsic.texi
index 3a105bc65f0..48c2d60826b 100644
--- a/gcc/fortran/intrinsic.texi
+++ b/gcc/fortran/intrinsic.texi
@@ -1547,7 +1547,7 @@ Fortran 90 and later
 
 
 @node ATAN
-@section @code{ATAN} --- Arctangent function 
+@section @code{ATAN} --- Arctangent function
 @fnindex ATAN
 @fnindex DATAN
 @cindex trigonometric function, tangent, inverse
@@ -1619,6 +1619,7 @@ Degrees function: @*
 @item @emph{Synopsis}:
 @multitable @columnfractions .80
 @item @code{RESULT = ATAND(X)}
+@item @code{RESULT = ATAND(Y, X)}
 @end multitable
 
 @item @emph{Description}:
@@ -1630,21 +1631,23 @@ Elemental function
 
 @item @emph{Arguments}:
 @multitable @columnfractions .15 .70
-@item @var{X} @tab The type shall be @code{REAL};
-if @var{Y} is present, @var{X} shall be REAL.
+@item @var{X} @tab The type shall be @code{REAL}.
 @item @var{Y} @tab The type and kind type parameter shall be the same as @var{X}.
 @end multitable
 
 @item @emph{Return value}:
 The return value is of the same type and kind as @var{X}.
-The result is in degrees and lies in the range
-@math{-90 \leq \Re \atand(x) \leq 90}.
+If @var{Y} is present, the result is identical to @code{ATAN2D(Y, X)}.
+Otherwise, the result is in degrees and lies in the range
+@math{-90 \leq \atand(x) \leq 90}.
 
 @item @emph{Example}:
 @smallexample
 program test_atand
   real(8) :: x = 2.866_8
+  real(4) :: x1 = 1.e0_4, y1 = 0.5e0_4
   x = atand(x)
+  x1 = atand(y1, x1)
 end program test_atand
 @end smallexample
 
diff --git a/gcc/testsuite/gfortran.dg/dec_math.f90 b/gcc/testsuite/gfortran.dg/dec_math.f90
index 393e7def88e..dbb4bdbefb9 100644
--- a/gcc/testsuite/gfortran.dg/dec_math.f90
+++ b/gcc/testsuite/gfortran.dg/dec_math.f90
@@ -521,6 +521,69 @@ call cmpq(q_i1, q_oe,  q_oc, q_tol, "(c) qatand")
 call cmpq(q_i1, q_oxe, q_ox, q_tol, "(x) qatand")
 #endif
 
+! Input
+f_i1 = 1.0_4
+f_i2 = 2.0_4
+d_i1 = 1.0_8
+d_i2 = 2.0_8
+#ifdef __GFC_REAL_10__
+l_i1 = 1.0_10
+l_i2 = 2.0_10
+#endif
+#ifdef __GFC_REAL_16__
+q_i1 = 1.0_16
+q_i2 = 2.0_16
+#endif
+
+! Expected
+f_oe  = r2d_f * atan2 (f_i1, f_i2)
+f_oxe = r2d_f * atan2 (xf * f_i1, f_i2)
+d_oe  = r2d_d * atan2 (d_i1, d_i2)
+d_oxe = r2d_d * atan2 (xd * d_i1, d_i2)
+#ifdef __GFC_REAL_10__
+l_oe  = r2d_l * atan2 (l_i1, l_i2)
+l_oxe = r2d_l * atan2 (xl * l_i1, l_i2)
+#endif
+#ifdef __GFC_REAL_16__
+q_oe  = r2d_q * atan2 (q_i1, q_i2)
+q_oxe = r2d_q * atan2 (xq * q_i1, q_i2)
+#endif
+
+! Actual
+f_oa = atand (f_i1, f_i2)
+f_oc = atand (1.0_4, 2.0_4)
+f_ox = atand (xf * f_i1, f_i2)
+d_oa = atand (d_i1, d_i2)
+d_oc = atand (1.0_8, 2.0_8)
+d_ox = atand (xd * d_i1, d_i2)
+#ifdef __GFC_REAL_10__
+l_oa = atand (l_i1, l_i2)
+l_oc = atand (1.0_10, 2.0_10)
+l_ox = atand (xl * l_i1, l_i2)
+#endif
+#ifdef __GFC_REAL_16__
+q_oa = atand (q_i1, q_i2)
+q_oc = atand (1.0_16, 2.0_16)
+q_ox = atand (xq * q_i1, q_i2)
+#endif
+
+call cmpf(f_i1, f_oe,  f_oa, f_tol, "( ) fatand")
+call cmpf(f_i1, f_oe,  f_oc, f_tol, "(c) fatand")
+call cmpf(f_i1, f_oxe, f_ox, f_tol, "(x) fatand")
+call cmpd(d_i1, d_oe,  d_oa, d_tol, "( ) datand")
+call cmpd(d_i1, d_oe,  d_oc, d_tol, "(c) datand")
+call cmpd(d_i1, d_oxe, d_ox, d_tol, "(x) atand")
+#ifdef __GFC_REAL_10__
+call cmpl(l_i1, l_oe,  l_oa, l_tol, "( ) latand")
+call cmpl(l_i1, l_oe,  l_oc, l_tol, "(c) latand")
+call cmpl(l_i1, l_oxe, l_ox, l_tol, "(x) latand")
+#endif
+#ifdef __GFC_REAL_16__
+call cmpq(q_i1, q_oe,  q_oa, q_tol, "( ) qatand")
+call cmpq(q_i1, q_oe,  q_oc, q_tol, "(c) qatand")
+call cmpq(q_i1, q_oxe, q_ox, q_tol, "(x) qatand")
+#endif
+
 ! Input
 f_i1 = 34.3775_4
 d_i1 = 34.3774677078494_8
-- 
2.43.0

