mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-25 18:10:21 -04:00 
			
		
		
		
	
		
			
	
	
		
			356 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			356 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | //  Copyright (c) 2007 John Maddock
 | ||
|  | //  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)
 | ||
|  | //
 | ||
|  | // Computes test data for the various bessel functions using
 | ||
|  | // archived - deliberately naive - version of the code.
 | ||
|  | // We'll rely on the high precision of mp_t to get us out of
 | ||
|  | // trouble and not worry about how long the calculations take.
 | ||
|  | // This provides a reasonably independent set of test data to
 | ||
|  | // compare against newly added asymptotic expansions etc.
 | ||
|  | //
 | ||
|  | #include <fstream>
 | ||
|  | 
 | ||
|  | #include <boost/math/tools/test_data.hpp>
 | ||
|  | #include <boost/math/special_functions/bessel.hpp>
 | ||
|  | #include "mp_t.hpp"
 | ||
|  | 
 | ||
|  | using namespace boost::math::tools; | ||
|  | using namespace boost::math; | ||
|  | using namespace boost::math::detail; | ||
|  | using namespace std; | ||
|  | 
 | ||
|  | // Compute J(v, x) and Y(v, x) simultaneously by Steed's method, see
 | ||
|  | // Barnett et al, Computer Physics Communications, vol 8, 377 (1974)
 | ||
|  | template <typename T> | ||
|  | int bessel_jy_bare(T v, T x, T* J, T* Y, int kind = need_j|need_y) | ||
|  | { | ||
|  |     // Jv1 = J_(v+1), Yv1 = Y_(v+1), fv = J_(v+1) / J_v
 | ||
|  |     // Ju1 = J_(u+1), Yu1 = Y_(u+1), fu = J_(u+1) / J_u
 | ||
|  |     T u, Jv, Ju, Yv, Yv1, Yu, Yu1, fv, fu; | ||
|  |     T W, p, q, gamma, current, prev, next; | ||
|  |     bool reflect = false; | ||
|  |     int n, k, s; | ||
|  | 
 | ||
|  |     using namespace std; | ||
|  |     using namespace boost::math::tools; | ||
|  |     using namespace boost::math::constants; | ||
|  | 
 | ||
|  |     if (v < 0) | ||
|  |     { | ||
|  |         reflect = true; | ||
|  |         v = -v;                             // v is non-negative from here
 | ||
|  |         kind = need_j|need_y;               // need both for reflection formula
 | ||
|  |     } | ||
|  |     n = real_cast<int>(v + 0.5L); | ||
|  |     u = v - n;                              // -1/2 <= u < 1/2
 | ||
|  | 
 | ||
|  |     if (x < 0) | ||
|  |     { | ||
|  |        *J = *Y = policies::raise_domain_error<T>("", | ||
|  |           "Real argument x=%1% must be non-negative, complex number result not supported", x, policies::policy<>()); | ||
|  |         return 1; | ||
|  |     } | ||
|  |     if (x == 0) | ||
|  |     { | ||
|  |        *J = *Y = policies::raise_overflow_error<T>( | ||
|  |           "", 0, policies::policy<>()); | ||
|  |        return 1; | ||
|  |     } | ||
|  | 
 | ||
|  |     // x is positive until reflection
 | ||
|  |     W = T(2) / (x * pi<T>());               // Wronskian
 | ||
|  |     if (x <= 2)                           // x in (0, 2]
 | ||
|  |     { | ||
|  |        if(temme_jy(u, x, &Yu, &Yu1, policies::policy<>()))             // Temme series
 | ||
|  |         { | ||
|  |            // domain error:
 | ||
|  |            *J = *Y = Yu; | ||
|  |            return 1; | ||
|  |         } | ||
|  |         prev = Yu; | ||
|  |         current = Yu1; | ||
|  |         for (k = 1; k <= n; k++)            // forward recurrence for Y
 | ||
|  |         { | ||
|  |             next = 2 * (u + k) * current / x - prev; | ||
|  |             prev = current; | ||
|  |             current = next; | ||
|  |         } | ||
|  |         Yv = prev; | ||
|  |         Yv1 = current; | ||
|  |         CF1_jy(v, x, &fv, &s, policies::policy<>());                 // continued fraction CF1
 | ||
|  |         Jv = W / (Yv * fv - Yv1);           // Wronskian relation
 | ||
|  |     } | ||
|  |     else                                    // x in (2, \infty)
 | ||
|  |     { | ||
|  |         // Get Y(u, x):
 | ||
|  |         CF1_jy(v, x, &fv, &s, policies::policy<>()); | ||
|  |         // tiny initial value to prevent overflow
 | ||
|  |         T init = sqrt(tools::min_value<T>()); | ||
|  |         prev = fv * s * init; | ||
|  |         current = s * init; | ||
|  |         for (k = n; k > 0; k--)             // backward recurrence for J
 | ||
|  |         { | ||
|  |             next = 2 * (u + k) * current / x - prev; | ||
|  |             prev = current; | ||
|  |             current = next; | ||
|  |         } | ||
|  |         T ratio = (s * init) / current;     // scaling ratio
 | ||
|  |         // can also call CF1() to get fu, not much difference in precision
 | ||
|  |         fu = prev / current; | ||
|  |         CF2_jy(u, x, &p, &q, policies::policy<>());                  // continued fraction CF2
 | ||
|  |         T t = u / x - fu;                   // t = J'/J
 | ||
|  |         gamma = (p - t) / q; | ||
|  |         Ju = sign(current) * sqrt(W / (q + gamma * (p - t))); | ||
|  | 
 | ||
|  |         Jv = Ju * ratio;                    // normalization
 | ||
|  | 
 | ||
|  |         Yu = gamma * Ju; | ||
|  |         Yu1 = Yu * (u/x - p - q/gamma); | ||
|  | 
 | ||
|  |         // compute Y:
 | ||
|  |         prev = Yu; | ||
|  |         current = Yu1; | ||
|  |         for (k = 1; k <= n; k++)            // forward recurrence for Y
 | ||
|  |         { | ||
|  |             next = 2 * (u + k) * current / x - prev; | ||
|  |             prev = current; | ||
|  |             current = next; | ||
|  |         } | ||
|  |         Yv = prev; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (reflect) | ||
|  |     { | ||
|  |         T z = (u + n % 2) * pi<T>(); | ||
|  |         *J = cos(z) * Jv - sin(z) * Yv;     // reflection formula
 | ||
|  |         *Y = sin(z) * Jv + cos(z) * Yv; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         *J = Jv; | ||
|  |         *Y = Yv; | ||
|  |     } | ||
|  | 
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | int progress = 0; | ||
|  | 
 | ||
|  | template <class T> | ||
|  | T cyl_bessel_j_bare(T v, T x) | ||
|  | { | ||
|  |    T j, y; | ||
|  |    bessel_jy_bare(v, x, &j, &y); | ||
|  | 
 | ||
|  |    std::cout << progress++ << ":   J(" << v << ", " << x << ") = " << j << std::endl; | ||
|  | 
 | ||
|  |    if(fabs(j) > 1e30) | ||
|  |       throw std::domain_error(""); | ||
|  | 
 | ||
|  |    return j; | ||
|  | } | ||
|  | 
 | ||
|  | template <class T> | ||
|  | T cyl_bessel_i_bare(T v, T x) | ||
|  | { | ||
|  |    using namespace std; | ||
|  |    if(x < 0) | ||
|  |    { | ||
|  |       // better have integer v:
 | ||
|  |       if(floor(v) == v) | ||
|  |       { | ||
|  |          T r = cyl_bessel_i_bare(v, -x); | ||
|  |          if(tools::real_cast<int>(v) & 1) | ||
|  |             r = -r; | ||
|  |          return r; | ||
|  |       } | ||
|  |       else | ||
|  |          return policies::raise_domain_error<T>( | ||
|  |             "", | ||
|  |             "Got x = %1%, but we need x >= 0", x, policies::policy<>()); | ||
|  |    } | ||
|  |    if(x == 0) | ||
|  |    { | ||
|  |       return (v == 0) ? 1 : 0; | ||
|  |    } | ||
|  |    T I, K; | ||
|  |    boost::math::detail::bessel_ik(v, x, &I, &K, 0xffff, policies::policy<>()); | ||
|  | 
 | ||
|  |    std::cout << progress++ << ":   I(" << v << ", " << x << ") = " << I << std::endl; | ||
|  | 
 | ||
|  |    if(fabs(I) > 1e30) | ||
|  |       throw std::domain_error(""); | ||
|  | 
 | ||
|  |    return I; | ||
|  | } | ||
|  | 
 | ||
|  | template <class T> | ||
|  | T cyl_bessel_k_bare(T v, T x) | ||
|  | { | ||
|  |    using namespace std; | ||
|  |    if(x < 0) | ||
|  |    { | ||
|  |       return policies::raise_domain_error<T>( | ||
|  |          "", | ||
|  |          "Got x = %1%, but we need x > 0", x, policies::policy<>()); | ||
|  |    } | ||
|  |    if(x == 0) | ||
|  |    { | ||
|  |       return (v == 0) ? policies::raise_overflow_error<T>("", 0, policies::policy<>()) | ||
|  |          : policies::raise_domain_error<T>( | ||
|  |          "", | ||
|  |          "Got x = %1%, but we need x > 0", x, policies::policy<>()); | ||
|  |    } | ||
|  |    T I, K; | ||
|  |    bessel_ik(v, x, &I, &K, 0xFFFF, policies::policy<>()); | ||
|  | 
 | ||
|  |    std::cout << progress++ << ":   K(" << v << ", " << x << ") = " << K << std::endl; | ||
|  | 
 | ||
|  |    if(fabs(K) > 1e30) | ||
|  |       throw std::domain_error(""); | ||
|  | 
 | ||
|  |    return K; | ||
|  | } | ||
|  | 
 | ||
|  | template <class T> | ||
|  | T cyl_neumann_bare(T v, T x) | ||
|  | { | ||
|  |    T j, y; | ||
|  |    bessel_jy(v, x, &j, &y, 0xFFFF, policies::policy<>()); | ||
|  | 
 | ||
|  |    std::cout << progress++ << ":   Y(" << v << ", " << x << ") = " << y << std::endl; | ||
|  | 
 | ||
|  |    if(fabs(y) > 1e30) | ||
|  |       throw std::domain_error(""); | ||
|  | 
 | ||
|  |    return y; | ||
|  | } | ||
|  | 
 | ||
|  | template <class T> | ||
|  | T sph_bessel_j_bare(T v, T x) | ||
|  | { | ||
|  |    std::cout << progress++ << ":   j(" << v << ", " << x << ") = "; | ||
|  |    if((v < 0) || (floor(v) != v)) | ||
|  |       throw std::domain_error(""); | ||
|  |    T r = sqrt(constants::pi<T>() / (2 * x)) * cyl_bessel_j_bare(v+0.5, x); | ||
|  |    std::cout << r << std::endl; | ||
|  |    return r; | ||
|  | } | ||
|  | 
 | ||
|  | template <class T> | ||
|  | T sph_bessel_y_bare(T v, T x) | ||
|  | { | ||
|  |    std::cout << progress++ << ":   y(" << v << ", " << x << ") = "; | ||
|  |    if((v < 0) || (floor(v) != v)) | ||
|  |       throw std::domain_error(""); | ||
|  |    T r = sqrt(constants::pi<T>() / (2 * x)) * cyl_neumann_bare(v+0.5, x); | ||
|  |    std::cout << r << std::endl; | ||
|  |    return r; | ||
|  | } | ||
|  | 
 | ||
|  | enum | ||
|  | { | ||
|  |    func_J = 0, | ||
|  |    func_Y, | ||
|  |    func_I, | ||
|  |    func_K, | ||
|  |    func_j, | ||
|  |    func_y | ||
|  | }; | ||
|  | 
 | ||
|  | int main(int argc, char* argv[]) | ||
|  | { | ||
|  |    std::cout << std::setprecision(17) << std::scientific; | ||
|  |    std::cout << sph_bessel_j_bare(0., 0.1185395751953125e4) << std::endl; | ||
|  |    std::cout << sph_bessel_j_bare(22., 0.6540834903717041015625) << std::endl; | ||
|  | 
 | ||
|  |    std::cout << std::setprecision(40) << std::scientific; | ||
|  | 
 | ||
|  |    parameter_info<mp_t> arg1, arg2; | ||
|  |    test_data<mp_t> data; | ||
|  | 
 | ||
|  |    int functype = 0; | ||
|  |    std::string letter = "J"; | ||
|  | 
 | ||
|  |    if(argc == 2) | ||
|  |    { | ||
|  |       if(std::strcmp(argv[1], "--Y") == 0) | ||
|  |       { | ||
|  |          functype = func_Y; | ||
|  |          letter = "Y"; | ||
|  |       } | ||
|  |       else if(std::strcmp(argv[1], "--I") == 0) | ||
|  |       { | ||
|  |          functype = func_I; | ||
|  |          letter = "I"; | ||
|  |       } | ||
|  |       else if(std::strcmp(argv[1], "--K") == 0) | ||
|  |       { | ||
|  |          functype = func_K; | ||
|  |          letter = "K"; | ||
|  |       } | ||
|  |       else if(std::strcmp(argv[1], "--j") == 0) | ||
|  |       { | ||
|  |          functype = func_j; | ||
|  |          letter = "j"; | ||
|  |       } | ||
|  |       else if(std::strcmp(argv[1], "--y") == 0) | ||
|  |       { | ||
|  |          functype = func_y; | ||
|  |          letter = "y"; | ||
|  |       } | ||
|  |       else | ||
|  |          assert(0); | ||
|  |    } | ||
|  | 
 | ||
|  |    bool cont; | ||
|  |    std::string line; | ||
|  | 
 | ||
|  |    std::cout << "Welcome.\n" | ||
|  |       "This program will generate spot tests for the Bessel " << letter << " function\n\n"; | ||
|  |    do{ | ||
|  |       get_user_parameter_info(arg1, "v"); | ||
|  |       get_user_parameter_info(arg2, "x"); | ||
|  |       mp_t (*fp)(mp_t, mp_t); | ||
|  |       if(functype == func_J)  | ||
|  |          fp = cyl_bessel_j_bare; | ||
|  |       else if(functype == func_I)  | ||
|  |          fp = cyl_bessel_i_bare; | ||
|  |       else if(functype == func_K)  | ||
|  |          fp = cyl_bessel_k_bare; | ||
|  |       else if(functype == func_Y) | ||
|  |          fp = cyl_neumann_bare; | ||
|  |       else if(functype == func_j) | ||
|  |          fp = sph_bessel_j_bare; | ||
|  |       else if(functype == func_y) | ||
|  |          fp = sph_bessel_y_bare; | ||
|  |       else | ||
|  |          assert(0); | ||
|  | 
 | ||
|  |       data.insert(fp, arg1, arg2); | ||
|  | 
 | ||
|  |       std::cout << "Any more data [y/n]?"; | ||
|  |       std::getline(std::cin, line); | ||
|  |       boost::algorithm::trim(line); | ||
|  |       cont = (line == "y"); | ||
|  |    }while(cont); | ||
|  | 
 | ||
|  |    std::cout << "Enter name of test data file [default=bessel_j_data.ipp]"; | ||
|  |    std::getline(std::cin, line); | ||
|  |    boost::algorithm::trim(line); | ||
|  |    if(line == "") | ||
|  |       line = "bessel_j_data.ipp"; | ||
|  |    std::ofstream ofs(line.c_str()); | ||
|  |    line.erase(line.find('.')); | ||
|  |    ofs << std::scientific << std::setprecision(40); | ||
|  |    write_code(ofs, data, line.c_str()); | ||
|  | 
 | ||
|  |    return 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 |