| /* |
| * Mathlib : A C Library of Special Functions |
| * Copyright (C) 1998-2015 Ross Ihaka and the R Core team. |
| * |
| * This program 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 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program 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 program; if not, a copy is available at |
| * https://www.R-project.org/Licenses/ |
| */ |
| |
| /* DESCRIPTION --> see below */ |
| |
| |
| /* From http://www.netlib.org/specfun/rjbesl Fortran translated by f2c,... |
| * ------------------------------=#---- Martin Maechler, ETH Zurich |
| * Additional code for nu == alpha < 0 MM |
| */ |
| #include "nmath.h" |
| #include "bessel.h" |
| |
| #ifndef MATHLIB_STANDALONE |
| #include <R_ext/Memory.h> |
| #endif |
| |
| #define min0(x, y) (((x) <= (y)) ? (x) : (y)) |
| |
| static void J_bessel(double *x, double *alpha, int *nb, |
| double *b, int *ncalc); |
| |
| // unused now from R |
| double bessel_j(double x, double alpha) |
| { |
| int nb, ncalc; |
| double na, *bj; |
| #ifndef MATHLIB_STANDALONE |
| const void *vmax; |
| #endif |
| |
| #ifdef IEEE_754 |
| /* NaNs propagated correctly */ |
| if (ISNAN(x) || ISNAN(alpha)) return x + alpha; |
| #endif |
| if (x < 0) { |
| ML_ERROR(ME_RANGE, "bessel_j"); |
| return ML_NAN; |
| } |
| na = floor(alpha); |
| if (alpha < 0) { |
| /* Using Abramowitz & Stegun 9.1.2 |
| * this may not be quite optimal (CPU and accuracy wise) */ |
| return(((alpha - na == 0.5) ? 0 : bessel_j(x, -alpha) * cospi(alpha)) + |
| ((alpha == na ) ? 0 : bessel_y(x, -alpha) * sinpi(alpha))); |
| } |
| else if (alpha > 1e7) { |
| MATHLIB_WARNING(_("besselJ(x, nu): nu=%g too large for bessel_j() algorithm"), |
| alpha); |
| return ML_NAN; |
| } |
| nb = 1 + (int)na; /* nb-1 <= alpha < nb */ |
| alpha -= (double)(nb-1); // ==> alpha' in [0, 1) |
| #ifdef MATHLIB_STANDALONE |
| bj = (double *) calloc(nb, sizeof(double)); |
| if (!bj) MATHLIB_ERROR("%s", _("bessel_j allocation error")); |
| #else |
| vmax = vmaxget(); |
| bj = (double *) R_alloc((size_t) nb, sizeof(double)); |
| #endif |
| J_bessel(&x, &alpha, &nb, bj, &ncalc); |
| if(ncalc != nb) {/* error input */ |
| if(ncalc < 0) |
| MATHLIB_WARNING4(_("bessel_j(%g): ncalc (=%d) != nb (=%d); alpha=%g. Arg. out of range?\n"), |
| x, ncalc, nb, alpha); |
| else |
| MATHLIB_WARNING2(_("bessel_j(%g,nu=%g): precision lost in result\n"), |
| x, alpha+(double)nb-1); |
| } |
| x = bj[nb-1]; |
| #ifdef MATHLIB_STANDALONE |
| free(bj); |
| #else |
| vmaxset(vmax); |
| #endif |
| return x; |
| } |
| |
| /* Called from R: modified version of bessel_j(), accepting a work array |
| * instead of allocating one. */ |
| double bessel_j_ex(double x, double alpha, double *bj) |
| { |
| int nb, ncalc; |
| double na; |
| |
| #ifdef IEEE_754 |
| /* NaNs propagated correctly */ |
| if (ISNAN(x) || ISNAN(alpha)) return x + alpha; |
| #endif |
| if (x < 0) { |
| ML_ERROR(ME_RANGE, "bessel_j"); |
| return ML_NAN; |
| } |
| na = floor(alpha); |
| if (alpha < 0) { |
| /* Using Abramowitz & Stegun 9.1.2 |
| * this may not be quite optimal (CPU and accuracy wise) */ |
| return(((alpha - na == 0.5) ? 0 : bessel_j_ex(x, -alpha, bj) * cospi(alpha)) + |
| ((alpha == na ) ? 0 : bessel_y_ex(x, -alpha, bj) * sinpi(alpha))); |
| } |
| else if (alpha > 1e7) { |
| MATHLIB_WARNING(_("besselJ(x, nu): nu=%g too large for bessel_j() algorithm"), |
| alpha); |
| return ML_NAN; |
| } |
| nb = 1 + (int)na; /* nb-1 <= alpha < nb */ |
| alpha -= (double)(nb-1); // ==> alpha' in [0, 1) |
| J_bessel(&x, &alpha, &nb, bj, &ncalc); |
| if(ncalc != nb) {/* error input */ |
| if(ncalc < 0) |
| MATHLIB_WARNING4(_("bessel_j(%g): ncalc (=%d) != nb (=%d); alpha=%g. Arg. out of range?\n"), |
| x, ncalc, nb, alpha); |
| else |
| MATHLIB_WARNING2(_("bessel_j(%g,nu=%g): precision lost in result\n"), |
| x, alpha+(double)nb-1); |
| } |
| x = bj[nb-1]; |
| return x; |
| } |
| |
| static void J_bessel(double *x, double *alpha, int *nb, |
| double *b, int *ncalc) |
| { |
| /* |
| Calculates Bessel functions J_{n+alpha} (x) |
| for non-negative argument x, and non-negative order n+alpha, n = 0,1,..,nb-1. |
| |
| Explanation of variables in the calling sequence. |
| |
| X - Non-negative argument for which J's are to be calculated. |
| ALPHA - Fractional part of order for which |
| J's are to be calculated. 0 <= ALPHA < 1. |
| NB - Number of functions to be calculated, NB >= 1. |
| The first function calculated is of order ALPHA, and the |
| last is of order (NB - 1 + ALPHA). |
| B - Output vector of length NB. If RJBESL |
| terminates normally (NCALC=NB), the vector B contains the |
| functions J/ALPHA/(X) through J/NB-1+ALPHA/(X). |
| NCALC - Output variable indicating possible errors. |
| Before using the vector B, the user should check that |
| NCALC=NB, i.e., all orders have been calculated to |
| the desired accuracy. See the following |
| |
| **************************************************************** |
| |
| Error return codes |
| |
| In case of an error, NCALC != NB, and not all J's are |
| calculated to the desired accuracy. |
| |
| NCALC < 0: An argument is out of range. For example, |
| NBES <= 0, ALPHA < 0 or > 1, or X is too large. |
| In this case, b[1] is set to zero, the remainder of the |
| B-vector is not calculated, and NCALC is set to |
| MIN(NB,0)-1 so that NCALC != NB. |
| |
| NB > NCALC > 0: Not all requested function values could |
| be calculated accurately. This usually occurs because NB is |
| much larger than ABS(X). In this case, b[N] is calculated |
| to the desired accuracy for N <= NCALC, but precision |
| is lost for NCALC < N <= NB. If b[N] does not vanish |
| for N > NCALC (because it is too small to be represented), |
| and b[N]/b[NCALC] = 10^(-K), then only the first NSIG - K |
| significant figures of b[N] can be trusted. |
| |
| |
| Acknowledgement |
| |
| This program is based on a program written by David J. Sookne |
| (2) that computes values of the Bessel functions J or I of float |
| argument and long order. Modifications include the restriction |
| of the computation to the J Bessel function of non-negative float |
| argument, the extension of the computation to arbitrary positive |
| order, and the elimination of most underflow. |
| |
| References: |
| |
| Olver, F.W.J., and Sookne, D.J. (1972) |
| "A Note on Backward Recurrence Algorithms"; |
| Math. Comp. 26, 941-947. |
| |
| Sookne, D.J. (1973) |
| "Bessel Functions of Real Argument and Integer Order"; |
| NBS Jour. of Res. B. 77B, 125-132. |
| |
| Latest modification: March 19, 1990 |
| |
| Author: W. J. Cody |
| Applied Mathematics Division |
| Argonne National Laboratory |
| Argonne, IL 60439 |
| ******************************************************************* |
| */ |
| |
| /* --------------------------------------------------------------------- |
| Mathematical constants |
| |
| PI2 = 2 / PI |
| TWOPI1 = first few significant digits of 2 * PI |
| TWOPI2 = (2*PI - TWOPI1) to working precision, i.e., |
| TWOPI1 + TWOPI2 = 2 * PI to extra precision. |
| --------------------------------------------------------------------- */ |
| const static double pi2 = .636619772367581343075535; |
| const static double twopi1 = 6.28125; |
| const static double twopi2 = .001935307179586476925286767; |
| |
| /*--------------------------------------------------------------------- |
| * Factorial(N) |
| *--------------------------------------------------------------------- */ |
| const static double fact[25] = { 1.,1.,2.,6.,24.,120.,720.,5040.,40320., |
| 362880.,3628800.,39916800.,479001600.,6227020800.,87178291200., |
| 1.307674368e12,2.0922789888e13,3.55687428096e14,6.402373705728e15, |
| 1.21645100408832e17,2.43290200817664e18,5.109094217170944e19, |
| 1.12400072777760768e21,2.585201673888497664e22, |
| 6.2044840173323943936e23 }; |
| |
| /* Local variables */ |
| int nend, intx, nbmx, i, j, k, l, m, n, nstart; |
| |
| double nu, twonu, capp, capq, pold, vcos, test, vsin; |
| double p, s, t, z, alpem, halfx, aa, bb, cc, psave, plast; |
| double tover, t1, alp2em, em, en, xc, xk, xm, psavel, gnu, xin, sum; |
| |
| |
| /* Parameter adjustment */ |
| --b; |
| |
| nu = *alpha; |
| twonu = nu + nu; |
| |
| /*------------------------------------------------------------------- |
| Check for out of range arguments. |
| -------------------------------------------------------------------*/ |
| if (*nb > 0 && *x >= 0. && 0. <= nu && nu < 1.) { |
| |
| *ncalc = *nb; |
| if(*x > xlrg_BESS_IJ) { |
| ML_ERROR(ME_RANGE, "J_bessel"); |
| /* indeed, the limit is 0, |
| * but the cutoff happens too early */ |
| for(i=1; i <= *nb; i++) |
| b[i] = 0.; /*was ML_POSINF (really nonsense) */ |
| return; |
| } |
| intx = (int) (*x); |
| /* Initialize result array to zero. */ |
| for (i = 1; i <= *nb; ++i) |
| b[i] = 0.; |
| |
| /*=================================================================== |
| Branch into 3 cases : |
| 1) use 2-term ascending series for small X |
| 2) use asymptotic form for large X when NB is not too large |
| 3) use recursion otherwise |
| ===================================================================*/ |
| |
| if (*x < rtnsig_BESS) { |
| /* --------------------------------------------------------------- |
| Two-term ascending series for small X. |
| --------------------------------------------------------------- */ |
| alpem = 1. + nu; |
| |
| halfx = (*x > enmten_BESS) ? .5 * *x : 0.; |
| aa = (nu != 0.) ? pow(halfx, nu) / (nu * Rf_gamma_cody(nu)) : 1.; |
| bb = (*x + 1. > 1.)? -halfx * halfx : 0.; |
| b[1] = aa + aa * bb / alpem; |
| if (*x != 0. && b[1] == 0.) |
| *ncalc = 0; |
| |
| if (*nb != 1) { |
| if (*x <= 0.) { |
| for (n = 2; n <= *nb; ++n) |
| b[n] = 0.; |
| } |
| else { |
| /* ---------------------------------------------- |
| Calculate higher order functions. |
| ---------------------------------------------- */ |
| if (bb == 0.) |
| tover = (enmten_BESS + enmten_BESS) / *x; |
| else |
| tover = enmten_BESS / bb; |
| cc = halfx; |
| for (n = 2; n <= *nb; ++n) { |
| aa /= alpem; |
| alpem += 1.; |
| aa *= cc; |
| if (aa <= tover * alpem) |
| aa = 0.; |
| |
| b[n] = aa + aa * bb / alpem; |
| if (b[n] == 0. && *ncalc > n) |
| *ncalc = n - 1; |
| } |
| } |
| } |
| } else if (*x > 25. && *nb <= intx + 1) { |
| /* ------------------------------------------------------------ |
| Asymptotic series for X > 25 (and not too large nb) |
| ------------------------------------------------------------ */ |
| xc = sqrt(pi2 / *x); |
| xin = 1 / (64 * *x * *x); |
| if (*x >= 130.) m = 4; |
| else if (*x >= 35.) m = 8; |
| else m = 11; |
| xm = 4. * (double) m; |
| /* ------------------------------------------------ |
| Argument reduction for SIN and COS routines. |
| ------------------------------------------------ */ |
| t = trunc(*x / (twopi1 + twopi2) + .5); |
| z = (*x - t * twopi1) - t * twopi2 - (nu + .5) / pi2; |
| vsin = sin(z); |
| vcos = cos(z); |
| gnu = twonu; |
| for (i = 1; i <= 2; ++i) { |
| s = (xm - 1. - gnu) * (xm - 1. + gnu) * xin * .5; |
| t = (gnu - (xm - 3.)) * (gnu + (xm - 3.)); |
| t1= (gnu - (xm + 1.)) * (gnu + (xm + 1.)); |
| k = m + m; |
| capp = s * t / fact[k]; |
| capq = s * t1/ fact[k + 1]; |
| xk = xm; |
| for (; k >= 4; k -= 2) {/* k + 2(j-2) == 2m */ |
| xk -= 4.; |
| s = (xk - 1. - gnu) * (xk - 1. + gnu); |
| t1 = t; |
| t = (gnu - (xk - 3.)) * (gnu + (xk - 3.)); |
| capp = (capp + 1. / fact[k - 2]) * s * t * xin; |
| capq = (capq + 1. / fact[k - 1]) * s * t1 * xin; |
| |
| } |
| capp += 1.; |
| capq = (capq + 1.) * (gnu * gnu - 1.) * (.125 / *x); |
| b[i] = xc * (capp * vcos - capq * vsin); |
| if (*nb == 1) |
| return; |
| |
| /* vsin <--> vcos */ t = vsin; vsin = -vcos; vcos = t; |
| gnu += 2.; |
| } |
| /* ----------------------------------------------- |
| If NB > 2, compute J(X,ORDER+I) for I = 2, NB-1 |
| ----------------------------------------------- */ |
| if (*nb > 2) |
| for (gnu = twonu + 2., j = 3; j <= *nb; j++, gnu += 2.) |
| b[j] = gnu * b[j - 1] / *x - b[j - 2]; |
| } |
| else { |
| /* rtnsig_BESS <= x && ( x <= 25 || intx+1 < *nb ) : |
| -------------------------------------------------------- |
| Use recurrence to generate results. |
| First initialize the calculation of P*S. |
| -------------------------------------------------------- */ |
| nbmx = *nb - intx; |
| n = intx + 1; |
| en = (double)(n + n) + twonu; |
| plast = 1.; |
| p = en / *x; |
| /* --------------------------------------------------- |
| Calculate general significance test. |
| --------------------------------------------------- */ |
| test = ensig_BESS + ensig_BESS; |
| if (nbmx >= 3) { |
| /* ------------------------------------------------------------ |
| Calculate P*S until N = NB-1. Check for possible overflow. |
| ---------------------------------------------------------- */ |
| tover = enten_BESS / ensig_BESS; |
| nstart = intx + 2; |
| nend = *nb - 1; |
| en = (double) (nstart + nstart) - 2. + twonu; |
| for (k = nstart; k <= nend; ++k) { |
| n = k; |
| en += 2.; |
| pold = plast; |
| plast = p; |
| p = en * plast / *x - pold; |
| if (p > tover) { |
| /* ------------------------------------------- |
| To avoid overflow, divide P*S by TOVER. |
| Calculate P*S until ABS(P) > 1. |
| -------------------------------------------*/ |
| tover = enten_BESS; |
| p /= tover; |
| plast /= tover; |
| psave = p; |
| psavel = plast; |
| nstart = n + 1; |
| do { |
| ++n; |
| en += 2.; |
| pold = plast; |
| plast = p; |
| p = en * plast / *x - pold; |
| } while (p <= 1.); |
| |
| bb = en / *x; |
| /* ----------------------------------------------- |
| Calculate backward test and find NCALC, |
| the highest N such that the test is passed. |
| ----------------------------------------------- */ |
| test = pold * plast * (.5 - .5 / (bb * bb)); |
| test /= ensig_BESS; |
| p = plast * tover; |
| --n; |
| en -= 2.; |
| nend = min0(*nb,n); |
| for (l = nstart; l <= nend; ++l) { |
| pold = psavel; |
| psavel = psave; |
| psave = en * psavel / *x - pold; |
| if (psave * psavel > test) { |
| *ncalc = l - 1; |
| goto L190; |
| } |
| } |
| *ncalc = nend; |
| goto L190; |
| } |
| } |
| n = nend; |
| en = (double) (n + n) + twonu; |
| /* ----------------------------------------------------- |
| Calculate special significance test for NBMX > 2. |
| -----------------------------------------------------*/ |
| test = fmax2(test, sqrt(plast * ensig_BESS) * sqrt(p + p)); |
| } |
| /* ------------------------------------------------ |
| Calculate P*S until significance test passes. */ |
| do { |
| ++n; |
| en += 2.; |
| pold = plast; |
| plast = p; |
| p = en * plast / *x - pold; |
| } while (p < test); |
| |
| L190: |
| /*--------------------------------------------------------------- |
| Initialize the backward recursion and the normalization sum. |
| --------------------------------------------------------------- */ |
| ++n; |
| en += 2.; |
| bb = 0.; |
| aa = 1. / p; |
| m = n / 2; |
| em = (double)m; |
| m = (n << 1) - (m << 2);/* = 2 n - 4 (n/2) |
| = 0 for even, 2 for odd n */ |
| if (m == 0) |
| sum = 0.; |
| else { |
| alpem = em - 1. + nu; |
| alp2em = em + em + nu; |
| sum = aa * alpem * alp2em / em; |
| } |
| nend = n - *nb; |
| /* if (nend > 0) */ |
| /* -------------------------------------------------------- |
| Recur backward via difference equation, calculating |
| (but not storing) b[N], until N = NB. |
| -------------------------------------------------------- */ |
| for (l = 1; l <= nend; ++l) { |
| --n; |
| en -= 2.; |
| cc = bb; |
| bb = aa; |
| aa = en * bb / *x - cc; |
| m = m ? 0 : 2; /* m = 2 - m failed on gcc4-20041019 */ |
| if (m != 0) { |
| em -= 1.; |
| alp2em = em + em + nu; |
| if (n == 1) |
| break; |
| |
| alpem = em - 1. + nu; |
| if (alpem == 0.) |
| alpem = 1.; |
| sum = (sum + aa * alp2em) * alpem / em; |
| } |
| } |
| /*-------------------------------------------------- |
| Store b[NB]. |
| --------------------------------------------------*/ |
| b[n] = aa; |
| if (nend >= 0) { |
| if (*nb <= 1) { |
| if (nu + 1. == 1.) |
| alp2em = 1.; |
| else |
| alp2em = nu; |
| sum += b[1] * alp2em; |
| goto L250; |
| } |
| else {/*-- nb >= 2 : --------------------------- |
| Calculate and store b[NB-1]. |
| ----------------------------------------*/ |
| --n; |
| en -= 2.; |
| b[n] = en * aa / *x - bb; |
| if (n == 1) |
| goto L240; |
| |
| m = m ? 0 : 2; /* m = 2 - m failed on gcc4-20041019 */ |
| if (m != 0) { |
| em -= 1.; |
| alp2em = em + em + nu; |
| alpem = em - 1. + nu; |
| if (alpem == 0.) |
| alpem = 1.; |
| sum = (sum + b[n] * alp2em) * alpem / em; |
| } |
| } |
| } |
| |
| /* if (n - 2 != 0) */ |
| /* -------------------------------------------------------- |
| Calculate via difference equation and store b[N], |
| until N = 2. |
| -------------------------------------------------------- */ |
| for (n = n-1; n >= 2; n--) { |
| en -= 2.; |
| b[n] = en * b[n + 1] / *x - b[n + 2]; |
| m = m ? 0 : 2; /* m = 2 - m failed on gcc4-20041019 */ |
| if (m != 0) { |
| em -= 1.; |
| alp2em = em + em + nu; |
| alpem = em - 1. + nu; |
| if (alpem == 0.) |
| alpem = 1.; |
| sum = (sum + b[n] * alp2em) * alpem / em; |
| } |
| } |
| /* --------------------------------------- |
| Calculate b[1]. |
| -----------------------------------------*/ |
| b[1] = 2. * (nu + 1.) * b[2] / *x - b[3]; |
| |
| L240: |
| em -= 1.; |
| alp2em = em + em + nu; |
| if (alp2em == 0.) |
| alp2em = 1.; |
| sum += b[1] * alp2em; |
| |
| L250: |
| /* --------------------------------------------------- |
| Normalize. Divide all b[N] by sum. |
| ---------------------------------------------------*/ |
| /* if (nu + 1. != 1.) poor test */ |
| if(fabs(nu) > 1e-15) |
| sum *= (Rf_gamma_cody(nu) * pow(.5* *x, -nu)); |
| |
| aa = enmten_BESS; |
| if (sum > 1.) |
| aa *= sum; |
| for (n = 1; n <= *nb; ++n) { |
| if (fabs(b[n]) < aa) |
| b[n] = 0.; |
| else |
| b[n] /= sum; |
| } |
| } |
| |
| } |
| else { |
| /* Error return -- X, NB, or ALPHA is out of range : */ |
| b[1] = 0.; |
| *ncalc = min0(*nb,0) - 1; |
| } |
| } |