// (C) Copyright John Maddock 2006. // (C) Copyright Matt Borland 2024. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_DECIMAL_DETAIL_CMATH_ASSOC_LEGENDRE_HPP #define BOOST_DECIMAL_DETAIL_CMATH_ASSOC_LEGENDRE_HPP #include #include #include #include #include #include #include #include #ifndef BOOST_DECIMAL_BUILD_MODULE #include #include #include #endif namespace boost { namespace decimal { namespace detail { template constexpr auto assoc_legendre_next(const unsigned l, const unsigned m, const T1 x, const T2 Pl, const T3 Plm1) noexcept { using result_type = promote_args_t; return ((2 * l + 1) * static_cast(x) * static_cast(Pl) - (l + m) * static_cast(Plm1)) / (l + 1 - m); } // Implement Legendre P and Q polynomials via recurrence: template constexpr auto assoc_legendre_impl(const unsigned l, const unsigned m, const T x, const T sin_theta_power) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { if (x < -1 || x > 1 || l > 128) { return std::numeric_limits::quiet_NaN(); } else if (isnan(x)) { return x; } if (l == 1 && m == 0) { return x; } else if (m > l) { return T{0}; } else if (m == 0) { return legendre(l, x); } // TODO(mborland): Once the lookup table has been exceeded we can calculate m!! but that is far more // complicated and computationally expensive BOOST_DECIMAL_ASSERT_MSG(m <= 50, "m > 50 has not been implemented"); T p0 = assoc_legendre_p0_lookup(2 * m - 1) * sin_theta_power; if (m & 1) { p0 = -p0; } if (m == l) { return p0; } T p1 = x * (2 * m + 1) * p0; auto n = m + 1; while (n < l) { std::swap(p0, p1); p1 = assoc_legendre_next(n, m, x, p0, p1); ++n; } return p1; } } //namespace detail BOOST_DECIMAL_EXPORT template constexpr auto assoc_legendre(const unsigned n, const unsigned m, const T x) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { using evaluation_type = detail::evaluation_type_t; return static_cast(detail::assoc_legendre_impl(n, m, static_cast(x), pow(1 - static_cast(x)*static_cast(x), evaluation_type{m} / 2))); } } //namespace decimal } //namespace boost #endif //BOOST_DECIMAL_DETAIL_CMATH_ASSOC_LEGENDRE_HPP