Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / math / example / jacobi_zeta_example.cpp
1 // Copyright Paul A. Bristow, 2019
2
3 // Use, modification and distribution are subject to the
4 // Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE_1_0.txt
6 // or copy at http://www.boost.org/LICENSE_1_0.txt)
7
8 /*! \title  Simple example of computation of the Jacobi Zeta function using Boost.Math,
9 and also using corresponding WolframAlpha commands.
10 */
11
12 #ifdef BOOST_NO_CXX11_NUMERIC_LIMITS
13 #  error "This example requires a C++ compiler that supports C++11 numeric_limits. Try C++11 or later."
14 #endif
15
16 #include <boost/math/special_functions/jacobi_zeta.hpp> // For jacobi_zeta function.
17 #include <boost/multiprecision/cpp_bin_float.hpp> // For cpp_bin_float_50.
18
19 #include <iostream>
20 #include <limits>
21 #include <iostream>
22 #include <exception>
23
24 int main()
25 {
26   try
27   {
28     std::cout.precision(std::numeric_limits<double>::max_digits10); // Show all potentially significant digits.
29     std::cout.setf(std::ios_base::showpoint); // Include any significant trailing zeros.
30
31     using boost::math::jacobi_zeta; // jacobi_zeta(T1 k, T2 phi) |k| <=1, k = sqrt(m)
32     using boost::multiprecision::cpp_bin_float_50;
33
34     // Wolfram Mathworld function JacobiZeta[phi, m] where m = k^2
35     // JacobiZeta[phi,m] gives the Jacobi zeta function Z(phi | m)
36
37     // If phi = 2, and elliptic modulus k = 0.9 so m = 0.9 * 0.9 = 0.81
38
39     // https://reference.wolfram.com/language/ref/JacobiZeta.html // Function information.
40     // A simple computation using phi = 2. and m = 0.9 * 0.9
41     // JacobiZeta[2, 0.9 * 0.9]
42     // https://www.wolframalpha.com/input/?i=JacobiZeta%5B2,+0.9+*+0.9%5D
43     // -0.248584...
44     // To get the expected 17 decimal digits precision for a 64-bit double type,
45     // we need to ask thus:
46     // N[JacobiZeta[2, 0.9 * 0.9],17]
47     // https://www.wolframalpha.com/input/?i=N%5BJacobiZeta%5B2,+0.9+*+0.9%5D,17%5D
48
49     double k = 0.9;
50     double m = k * k;
51     double phi = 2.;
52
53     std::cout << "m = k^2 = " << m << std::endl;  // m = k^2 =  0.81000000000000005
54     std::cout << "jacobi_zeta(" << k << ", " << phi << " )  = " << jacobi_zeta(k, phi) << std::endl;
55     // jacobi_zeta(0.90000000000000002, 2.0000000000000000 )  =
56     //  -0.24858442708494899  Boost.Math
57     //  -0.24858442708494893  Wolfram
58     // that agree within the expected precision of 17 decimal digits for 64-bit type double.
59
60     // We can also easily get a higher precision too:
61     // For example, to get 50 decimal digit precision using WolframAlpha:
62     // N[JacobiZeta[2, 0.9 * 0.9],50]
63     // https://www.wolframalpha.com/input/?i=N%5BJacobiZeta%5B2,+0.9+*+0.9%5D,50%5D
64     // -0.24858442708494893408462856109734087389683955309853
65
66     // Using Boost.Multiprecision we can do them same almost as easily.
67
68     // To check that we are not losing precision, we show all the significant digits of the arguments ad result:
69     std::cout.precision(std::numeric_limits<cpp_bin_float_50>::digits10); // Show all significant digits.
70
71     // We can force the computation to use 50 decimal digit precision thus:
72     cpp_bin_float_50 k50("0.9");
73     cpp_bin_float_50 phi50("2.");
74
75     std::cout << "jacobi_zeta(" << k50 << ", " << phi50 << " )  = " << jacobi_zeta(k50, phi50) << std::endl;
76     // jacobi_zeta(0.90000000000000000000000000000000000000000000000000,
77     //   2.0000000000000000000000000000000000000000000000000 )
78     //   = -0.24858442708494893408462856109734087389683955309853
79
80     // and a comparison with Wolfram shows agreement to the expected precision.
81     // -0.24858442708494893408462856109734087389683955309853  Boost.Math
82     // -0.24858442708494893408462856109734087389683955309853  Wolfram
83
84     // Taking care not to fall into the awaiting pit, we ensure that ALL arguments passed are of the
85     // appropriate 50-digit precision and do NOT suffer from precision reduction to that of type double,
86     // We do NOT write:
87     std::cout << "jacobi_zeta<cpp_bin_float_50>(0.9, 2.)  = " << jacobi_zeta<cpp_bin_float_50>(0.9, 2) << std::endl;
88     // jacobi_zeta(0.90000000000000000000000000000000000000000000000000,
89     //   2.0000000000000000000000000000000000000000000000000 )
90     //   = -0.24858442708494895921459900494815797085727097762164  << Wrong at about 17th digit!
91     //     -0.24858442708494893408462856109734087389683955309853  Wolfram
92   }
93   catch (std::exception const& ex)
94   {
95     // Lacking try&catch blocks, the program will abort after any throw, whereas the
96     // message below from the thrown exception will give some helpful clues as to the cause of the problem.
97     std::cout << "\n""Message from thrown exception was:\n   " << ex.what() << std::endl;
98     // An example of message:
99     // std::cout << " = " << jacobi_zeta(2, 0.5) << std::endl;
100     // Message from thrown exception was:
101     // Error in function boost::math::ellint_k<long double>(long double) : Got k = 2, function requires |k| <= 1
102     // Shows that first parameter is k and is out of range, as the definition in docs jacobi_zeta(T1 k, T2 phi);
103   }
104 } // int main()