From f29a1ef2d8efb9423b878c9d5da1961527fce707 Mon Sep 17 00:00:00 2001
From: Edward Smith-Rowland <3dw4rd@verizon.net>
Date: Wed, 6 Mar 2019 13:38:32 +0000
Subject: [PATCH] PR libstdc++/86655 - std::assoc_legendre should not constrain

2019-03-06  Edward Smith-Rowland  <3dw4rd@verizon.net>

	PR libstdc++/86655 - std::assoc_legendre should not constrain
	the value of m (or x).
	* include/tr1/legendre_function.tcc (__assoc_legendre_p,
	__sph_legendre): If degree > order Don't throw, return 0.
	(__legendre_p, __assoc_legendre_p): Don't constrain x either.
	* testsuite/special_functions/02_assoc_legendre/pr86655.cc: New test.
	* testsuite/special_functions/20_sph_legendre/pr86655.cc: New test.
	* testsuite/tr1/5_numerical_facilities/special_functions/
	02_assoc_legendre/pr86655.cc: New test.
	* testsuite/tr1/5_numerical_facilities/special_functions/
	22_sph_legendre/pr86655.cc: New test.

From-SVN: r269423
---
 libstdc++-v3/ChangeLog                        | 14 +++++
 .../include/tr1/legendre_function.tcc         | 28 +++-------
 .../02_assoc_legendre/pr86655.cc              | 56 +++++++++++++++++++
 .../20_sph_legendre/pr86655.cc                | 56 +++++++++++++++++++
 .../02_assoc_legendre/pr86655.cc              | 56 +++++++++++++++++++
 .../22_sph_legendre/pr86655.cc                | 56 +++++++++++++++++++
 6 files changed, 247 insertions(+), 19 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/special_functions/02_assoc_legendre/pr86655.cc
 create mode 100644 libstdc++-v3/testsuite/special_functions/20_sph_legendre/pr86655.cc
 create mode 100644 libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/pr86655.cc
 create mode 100644 libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/pr86655.cc

diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 7a199f8fc191..4cbdc80aa08d 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,17 @@
+2019-03-06  Edward Smith-Rowland  <3dw4rd@verizon.net>
+
+	PR libstdc++/86655 - std::assoc_legendre should not constrain
+	the value of m (or x).
+	* include/tr1/legendre_function.tcc (__assoc_legendre_p,
+	__sph_legendre): If degree > order Don't throw, return 0.
+	(__legendre_p, __assoc_legendre_p): Don't constrain x either.
+	* testsuite/special_functions/02_assoc_legendre/pr86655.cc: New test.
+	* testsuite/special_functions/20_sph_legendre/pr86655.cc: New test.
+	* testsuite/tr1/5_numerical_facilities/special_functions/
+	02_assoc_legendre/pr86655.cc: New test.
+	* testsuite/tr1/5_numerical_facilities/special_functions/
+	22_sph_legendre/pr86655.cc: New test.
+
 2019-03-06  Ville Voutilainen  <ville.voutilainen@gmail.com>
 
 	Rewrite variant.
diff --git a/libstdc++-v3/include/tr1/legendre_function.tcc b/libstdc++-v3/include/tr1/legendre_function.tcc
index 33d933a1238d..f782907a67df 100644
--- a/libstdc++-v3/include/tr1/legendre_function.tcc
+++ b/libstdc++-v3/include/tr1/legendre_function.tcc
@@ -82,10 +82,7 @@ namespace tr1
     __poly_legendre_p(unsigned int __l, _Tp __x)
     {
 
-      if ((__x < _Tp(-1)) || (__x > _Tp(+1)))
-        std::__throw_domain_error(__N("Argument out of range"
-                                      " in __poly_legendre_p."));
-      else if (__isnan(__x))
+      if (__isnan(__x))
         return std::numeric_limits<_Tp>::quiet_NaN();
       else if (__x == +_Tp(1))
         return +_Tp(1);
@@ -126,11 +123,11 @@ namespace tr1
      *   @f[
      *     P_l^m(x) = (1 - x^2)^{m/2}\frac{d^m}{dx^m}P_l(x)
      *   @f]
+     *   @note @f$ P_l^m(x) = 0 @f$ if @f$ m > l @f$.
      * 
      *   @param  l  The degree of the associated Legendre function.
      *              @f$ l >= 0 @f$.
      *   @param  m  The order of the associated Legendre function.
-     *              @f$ m <= l @f$.
      *   @param  x  The argument of the associated Legendre function.
      *              @f$ |x| <= 1 @f$.
      *   @param  phase  The phase of the associated Legendre function.
@@ -142,12 +139,8 @@ namespace tr1
 		       _Tp __phase = _Tp(+1))
     {
 
-      if (__x < _Tp(-1) || __x > _Tp(+1))
-        std::__throw_domain_error(__N("Argument out of range"
-                                      " in __assoc_legendre_p."));
-      else if (__m > __l)
-        std::__throw_domain_error(__N("Degree out of range"
-                                      " in __assoc_legendre_p."));
+      if (__m > __l)
+        return _Tp(0);
       else if (__isnan(__x))
         return std::numeric_limits<_Tp>::quiet_NaN();
       else if (__m == 0)
@@ -209,12 +202,12 @@ namespace tr1
      *   and so this function is stable for larger differences of @f$ l @f$
      *   and @f$ m @f$.
      *   @note Unlike the case for __assoc_legendre_p the Condon-Shortley
-     *   phase factor @f$ (-1)^m @f$ is present here.
+     *         phase factor @f$ (-1)^m @f$ is present here.
+     *   @note @f$ Y_l^m(\theta) = 0 @f$ if @f$ m > l @f$.
      * 
      *   @param  l  The degree of the spherical associated Legendre function.
      *              @f$ l >= 0 @f$.
      *   @param  m  The order of the spherical associated Legendre function.
-     *              @f$ m <= l @f$.
      *   @param  theta  The radian angle argument of the spherical associated
      *                  Legendre function.
      */
@@ -227,11 +220,8 @@ namespace tr1
 
       const _Tp __x = std::cos(__theta);
 
-      if (__l < __m)
-        {
-          std::__throw_domain_error(__N("Bad argument "
-                                        "in __sph_legendre."));
-        }
+      if (__m > __l)
+        return _Tp(0);
       else if (__m == 0)
         {
           _Tp __P = __poly_legendre_p(__l, __x);
@@ -284,7 +274,7 @@ namespace tr1
               _Tp __y_lm = _Tp(0);
 
               // Compute Y_l^m, l > m+1, upward recursion on l.
-              for (unsigned int __ll = __m + 2; __ll <= __l; ++__ll)
+              for (int __ll = __m + 2; __ll <= __l; ++__ll)
                 {
                   const _Tp __rat1 = _Tp(__ll - __m) / _Tp(__ll + __m);
                   const _Tp __rat2 = _Tp(__ll - __m - 1) / _Tp(__ll + __m - 1);
diff --git a/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/pr86655.cc b/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/pr86655.cc
new file mode 100644
index 000000000000..2b028348ba73
--- /dev/null
+++ b/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/pr86655.cc
@@ -0,0 +1,56 @@
+// { dg-do run { target c++11 } }
+// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__ -ffp-contract=off" }
+
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <initializer_list>
+#include <cmath>
+#if defined(__TEST_DEBUG)
+#  include <iostream>
+#  define VERIFY(A) \
+  if (!(A)) \
+    { \
+      std::cout << "line " << __LINE__ \
+	<< "  std::assoc_legendre(l, m, x) == 0: " << (A) \
+	<< '\n'; \
+    }
+#else
+#  include <testsuite_hooks.h>
+#endif
+
+template<typename _Tp>
+  void
+  test_m_gt_l()
+  {
+    bool test __attribute__((unused)) = true;
+    for (auto l : {0u, 1u, 2u, 5u})
+      for (auto m : {l + 1u, l + 2u})
+	for (auto i : {-2, -1, 0, 1, 2})
+	  {
+	    auto x = _Tp(i * 0.5L);
+	    VERIFY(std::assoc_legendre(l, m, x) == _Tp(0));
+	  }
+  }
+
+int
+main()
+{
+  test_m_gt_l<float>();
+  test_m_gt_l<double>();
+  test_m_gt_l<long double>();
+}
diff --git a/libstdc++-v3/testsuite/special_functions/20_sph_legendre/pr86655.cc b/libstdc++-v3/testsuite/special_functions/20_sph_legendre/pr86655.cc
new file mode 100644
index 000000000000..94ee0d6ce6c7
--- /dev/null
+++ b/libstdc++-v3/testsuite/special_functions/20_sph_legendre/pr86655.cc
@@ -0,0 +1,56 @@
+// { dg-do run { target c++11 } }
+// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__ -ffp-contract=off" }
+
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <initializer_list>
+#include <cmath>
+#if defined(__TEST_DEBUG)
+#  include <iostream>
+#  define VERIFY(A) \
+  if (!(A)) \
+    { \
+      std::cout << "line " << __LINE__ \
+	<< "  std::sph_legendre(l, m, x) == 0: " << (A) \
+	<< '\n'; \
+    }
+#else
+#  include <testsuite_hooks.h>
+#endif
+
+template<typename _Tp>
+  void
+  test_m_gt_l()
+  {
+    bool test __attribute__((unused)) = true;
+    for (auto l : {0u, 1u, 2u, 5u})
+      for (auto m : {l + 1u, l + 2u})
+	for (auto i : {-2, -1, 0, 1, 2})
+	  {
+	    auto theta = std::acos(_Tp(i * 0.5L));
+	    VERIFY(std::sph_legendre(l, m, theta) == _Tp(0));
+	  }
+  }
+
+int
+main()
+{
+  test_m_gt_l<float>();
+  test_m_gt_l<double>();
+  test_m_gt_l<long double>();
+}
diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/pr86655.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/pr86655.cc
new file mode 100644
index 000000000000..092afddbba59
--- /dev/null
+++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/pr86655.cc
@@ -0,0 +1,56 @@
+// { dg-do run }
+// { dg-options "-std=c++98 -ffp-contract=off" }
+
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <tr1/cmath>
+#if defined(__TEST_DEBUG)
+#  include <iostream>
+#  define VERIFY(A) \
+  if (!(A)) \
+    { \
+      std::cout << "line " << __LINE__ \
+	<< "  std::tr1::assoc_legendre(l, m, x) == 0: " << (A) \
+	<< '\n'; \
+    }
+#else
+#  include <testsuite_hooks.h>
+#endif
+
+template<typename _Tp>
+  void
+  test_m_gt_l()
+  {
+    bool test __attribute__((unused)) = true;
+    unsigned int larr[4] = {0u, 1u, 2u, 5u};
+    for (unsigned int l = 0; l < 4; ++l)
+      for (unsigned int m = larr[l] + 1u; m <= larr[l] + 2u; ++m)
+	for (int i = -2; i <= +2; ++i)
+	  {
+	    _Tp x = _Tp(i * 0.5L);
+	    VERIFY(std::tr1::assoc_legendre(larr[l], m, x) == _Tp(0));
+	  }
+  }
+
+int
+main()
+{
+  test_m_gt_l<float>();
+  test_m_gt_l<double>();
+  test_m_gt_l<long double>();
+}
diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/pr86655.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/pr86655.cc
new file mode 100644
index 000000000000..510044378add
--- /dev/null
+++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/pr86655.cc
@@ -0,0 +1,56 @@
+// { dg-do run }
+// { dg-options "-std=c++98 -ffp-contract=off" }
+
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <tr1/cmath>
+#if defined(__TEST_DEBUG)
+#  include <iostream>
+#  define VERIFY(A) \
+  if (!(A)) \
+    { \
+      std::cout << "line " << __LINE__ \
+	<< "  std::sph_legendre(l, m, x) == 0: " << (A) \
+	<< '\n'; \
+    }
+#else
+#  include <testsuite_hooks.h>
+#endif
+
+template<typename _Tp>
+  void
+  test_m_gt_l()
+  {
+    bool test __attribute__((unused)) = true;
+    unsigned int larr[4] = {0u, 1u, 2u, 5u};
+    for (unsigned int l = 0; l < 4; ++l)
+      for (unsigned int m = larr[l] + 1u; m <= larr[l] + 2u; ++m)
+	for (int i = -2; i <= +2; ++i)
+	  {
+	    _Tp theta = std::acos(_Tp(i * 0.5L));
+	    VERIFY(std::tr1::sph_legendre(larr[l], m, theta) == _Tp(0));
+	  }
+  }
+
+int
+main()
+{
+  test_m_gt_l<float>();
+  test_m_gt_l<double>();
+  test_m_gt_l<long double>();
+}
-- 
GitLab