diff --git a/include/boost/math/special_functions/jacobi.hpp b/include/boost/math/special_functions/jacobi.hpp index 6451ce978..4c744af2f 100644 --- a/include/boost/math/special_functions/jacobi.hpp +++ b/include/boost/math/special_functions/jacobi.hpp @@ -37,5 +37,19 @@ Real jacobi(unsigned n, Real alpha, Real beta, Real x) return yk; } +template +Real jacobi_derivative(unsigned n, Real alpha, Real beta, Real x, unsigned k) +{ + if (k > n) { + return Real(0); + } + Real scale = 1; + for(unsigned j = 1; j <= k; ++j) { + scale *= (alpha + beta + n + j)/2; + } + + return scale*jacobi(n-k, alpha + k, beta+k, x); +} + }} #endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 53905d8d1..bf76ce0e0 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -939,6 +939,7 @@ test-suite misc : [ run test_vector_barycentric_rational.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_smart_ptr cxx11_defaulted_functions cxx11_auto_declarations cxx11_unified_initialization_syntax ] [ check-target-builds ../../multiprecision/config//has_eigen : : no ] ] [ run test_cubic_b_spline.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_smart_ptr cxx11_defaulted_functions ] off msvc:/bigobj release ] [ run cardinal_b_spline_test.cpp : : : [ requires cxx11_auto_declarations cxx11_constexpr cxx11_smart_ptr cxx11_defaulted_functions ] [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : -lquadmath ] ] + [ run jacobi_test.cpp : : : [ requires cxx11_auto_declarations cxx11_constexpr cxx11_smart_ptr cxx11_defaulted_functions ] [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : -lquadmath ] ] [ run whittaker_shannon_test.cpp : : : [ requires cxx11_auto_declarations cxx11_constexpr cxx11_smart_ptr cxx11_defaulted_functions ] ] [ run cardinal_quadratic_b_spline_test.cpp : : : [ requires cxx11_auto_declarations cxx11_constexpr cxx11_smart_ptr cxx11_defaulted_functions ] ] [ run catmull_rom_test.cpp ../../test/build//boost_unit_test_framework : : : TEST=1 [ requires cxx11_hdr_array cxx11_hdr_initializer_list ] : catmull_rom_test_1 ] diff --git a/test/jacobi_test.cpp b/test/jacobi_test.cpp new file mode 100644 index 000000000..2a3d52f9d --- /dev/null +++ b/test/jacobi_test.cpp @@ -0,0 +1,118 @@ +/* + * Copyright Nick Thompson, 2019 + * 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) + */ + +#include "math_unit_test.hpp" +#include +#include +#include +#include +#include +#ifdef BOOST_HAS_FLOAT128 +#include +using boost::multiprecision::float128; +#endif + +using std::abs; +using boost::math::jacobi; +using boost::math::jacobi_derivative; + +template +void test_to_quadratic() +{ + Real h = 1/Real(8); + for (Real alpha = -1 + h; alpha < 2; alpha += h) { + for (Real beta = -1 + h; beta < 2; beta += h) { + for (Real x = -1; x < 1; x += h) { + Real expected = 1; + Real computed = jacobi(0, alpha, beta, x); + CHECK_ULP_CLOSE(expected, computed, 0); + + expected = (alpha + 1) + (alpha + beta +2)*(x-1)/2; + computed = jacobi(1, alpha, beta, x); + CHECK_ULP_CLOSE(expected, computed, 0); + + expected = (alpha + 1)*(alpha+2)/2 + (alpha + 2)*(alpha + beta + 3)*(x-1)/2 + (alpha + beta + 3)*(alpha + beta + 4)*(x-1)*(x-1)/8; + computed = jacobi(2, alpha, beta, x); + CHECK_ULP_CLOSE(expected, computed, 1); + + } + } + } +} + +template +void test_symmetry() +{ + Real h = 1/Real(4); + for (Real alpha = -1 + h; alpha < 2; alpha += h) { + for (Real beta = -1 + h; beta < 2; beta += h) { + for (Real x = -1; x < 1; x += h) { + for (size_t n = 0; n < 20; n += 2) + { + Real expected = jacobi(n, beta, alpha , -x); + Real computed = jacobi(n, alpha, beta, x); + CHECK_ULP_CLOSE(expected, computed, 0); + + expected = jacobi(n+1, beta, alpha, -x); + computed = -jacobi(n+1, alpha, beta, x); + CHECK_ULP_CLOSE(expected, computed, 0); + } + } + } + } +} + +template +void test_derivative() +{ + Real h = 1/Real(4); + for (Real alpha = -1 + h; alpha < 2; alpha += h) { + for (Real beta = -1 + h; beta < 2; beta += h) { + for (Real x = -1; x < 1; x += h) { + Real expected = 0; + Real computed = jacobi_derivative(0, alpha, beta, x, 1); + CHECK_ULP_CLOSE(expected, computed, 0); + + expected = (alpha + beta + 2)/2; + computed = jacobi_derivative(1, alpha, beta, x, 1); + CHECK_ULP_CLOSE(expected, computed, 0); + + expected = (alpha + 2)*(alpha + beta + 3)/2 + (alpha + beta + 3)*(alpha + beta + 4)*(x-1)/4; + computed = jacobi_derivative(2, alpha, beta, x, 1); + CHECK_ULP_CLOSE(expected, computed, 0); + + expected = (alpha + beta + 3)*(alpha + beta + 4)/4; + computed = jacobi_derivative(2, alpha, beta, x, 2); + CHECK_ULP_CLOSE(expected, computed, 0); + } + } + } +} + +int main() +{ + + test_to_quadratic(); + test_to_quadratic(); + test_to_quadratic(); + + test_symmetry(); + test_symmetry(); + test_symmetry(); + + test_derivative(); + test_derivative(); + test_derivative(); + +#ifdef BOOST_HAS_FLOAT128 + test_to_quadratic(); + test_symmetry(); + test_derivative(); +#endif + + return boost::math::test::report_errors(); +}