[gnumeric] Bessel: fix intermediate overflow and underflow.



commit a9894d84e1411b4cc6f0021b44534da56440d58f
Author: Morten Welinder <terra gnome org>
Date:   Thu Jan 28 09:37:24 2016 -0500

    Bessel: fix intermediate overflow and underflow.

 ChangeLog       |    3 +
 NEWS            |    2 +
 src/sf-bessel.c | 2341 ++++++++++++++++++++++++++++---------------------------
 3 files changed, 1218 insertions(+), 1128 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 678f382..af2e1d9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,9 @@
 
 2016-01-27  Morten Welinder  <terra gnome org>
 
+       * src/sf-bessel.c (gnm_bessel_j, gnm_bessel_y): New
+       implementation.
+
        * src/wbc-gtk.c (cb_add_menus_toolbars): Work around gtk+ bug with
        css styling.
 
diff --git a/NEWS b/NEWS
index 1e382b0..48eed4e 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,8 @@ Morten:
        * Fix R.DBINOM extreme-value case.  [#760230]
        * New function AGM.
        * Fix canvas problem leaving grab in place.  [#760639]
+       * Work around gtk+ bug causing growing windows.  [#761142]
+       * Improve BESSELJ and BESSELY.
 
 --------------------------------------------------------------------------
 Gnumeric 1.12.26
diff --git a/src/sf-bessel.c b/src/sf-bessel.c
index b49efe6..17471cf 100644
--- a/src/sf-bessel.c
+++ b/src/sf-bessel.c
@@ -11,17 +11,20 @@
 #define MATHLIB_WARNING2 g_warning
 #define MATHLIB_WARNING4 g_warning
 
-static gboolean bessel_j_series_domain (gnm_float x, gnm_float v);
-static gnm_float bessel_j_series (gnm_float x, gnm_float alpha);
-
-static gnm_float bessel_y_ex(gnm_float x, gnm_float alpha, gnm_float *by);
 static gnm_float bessel_k(gnm_float x, gnm_float alpha, gnm_float expo);
-static gnm_float bessel_y(gnm_float x, gnm_float alpha);
 
 static inline int imin2 (int x, int y) { return MIN (x, y); }
 static inline int imax2 (int x, int y) { return MAX (x, y); }
 static inline gnm_float fmax2 (gnm_float x, gnm_float y) { return MAX (x, y); }
 
+static gnm_float
+gnm_cbrt (gnm_float x)
+{
+       gnm_float ar = gnm_pow (gnm_abs (x), 1.0 / 3.0);
+       return x < 0 ? 0 - ar : ar;
+}
+
+
 /* ------------------------------------------------------------------------- */
 
 /* Imported src/nmath/bessel.h from R.  */
@@ -626,583 +629,6 @@ L230:
 }
 
 /* ------------------------------------------------------------------------ */
-/* Imported src/nmath/bessel_j.c from R.  */
-/*
- *  Mathlib : A C Library of Special Functions
- *  Copyright (C) 1998-2012 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
- *  http://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
- */
-
-#ifndef MATHLIB_STANDALONE
-#endif
-
-#define min0(x, y) (((x) <= (y)) ? (x) : (y))
-
-static void J_bessel(gnm_float *x, gnm_float *alpha, long *nb,
-                    gnm_float *b, long *ncalc);
-
-static gnm_float bessel_j(gnm_float x, gnm_float alpha)
-{
-    long nb, ncalc;
-    gnm_float na, *bj;
-#ifndef MATHLIB_STANDALONE
-    const void *vmax;
-#endif
-
-#ifdef IEEE_754
-    /* NaNs propagated correctly */
-    if (gnm_isnan(x) || gnm_isnan(alpha)) return x + alpha;
-#endif
-    if (x < 0) {
-       ML_ERROR(ME_RANGE);
-       return gnm_nan;
-    }
-    na = gnm_floor(alpha);
-    if (alpha < 0) {
-       /* Using Abramowitz & Stegun  9.1.2
-        * this may not be quite optimal (CPU and accuracy wise) */
-       return(bessel_j(x, -alpha) * gnm_cospi(alpha) +
-              ((alpha == na) ? 0 :
-              bessel_y(x, -alpha) * gnm_sinpi(alpha)));
-    }
-    if (bessel_j_series_domain (x, alpha))
-           return bessel_j_series (x, alpha);
-
-    nb = 1 + (long)na; /* nb-1 <= alpha < nb */
-    alpha -= (gnm_float)(nb-1);
-#ifdef MATHLIB_STANDALONE
-    bj = (gnm_float *) calloc(nb, sizeof(gnm_float));
-    if (!bj) MATHLIB_ERROR("%s", ("bessel_j allocation error"));
-#else
-    vmax = vmaxget();
-    bj = (gnm_float *) R_alloc((size_t) nb, sizeof(gnm_float));
-#endif
-    J_bessel(&x, &alpha, &nb, bj, &ncalc);
-    if(ncalc != nb) {/* error input */
-      if(ncalc < 0)
-       MATHLIB_WARNING4(("bessel_j(%" GNM_FORMAT_g "): ncalc (=%ld) != nb (=%ld); alpha=%" GNM_FORMAT_g ". 
Arg. out of range?\n"),
-                        x, ncalc, nb, alpha);
-      else
-       MATHLIB_WARNING2(("bessel_j(%" GNM_FORMAT_g ",nu=%" GNM_FORMAT_g "): precision lost in result\n"),
-                        x, alpha+(gnm_float)nb-1);
-    }
-    x = bj[nb-1];
-#ifdef MATHLIB_STANDALONE
-    free(bj);
-#else
-    vmaxset(vmax);
-#endif
-    return x;
-}
-
-/* modified version of bessel_j that accepts a work array instead of
-   allocating one. */
-static gnm_float bessel_j_ex(gnm_float x, gnm_float alpha, gnm_float *bj)
-{
-    long nb, ncalc;
-    gnm_float na;
-
-#ifdef IEEE_754
-    /* NaNs propagated correctly */
-    if (gnm_isnan(x) || gnm_isnan(alpha)) return x + alpha;
-#endif
-    if (x < 0) {
-       ML_ERROR(ME_RANGE);
-       return gnm_nan;
-    }
-    na = gnm_floor(alpha);
-    if (alpha < 0) {
-       /* Using Abramowitz & Stegun  9.1.2
-        * this may not be quite optimal (CPU and accuracy wise) */
-       return(bessel_j_ex(x, -alpha, bj) * gnm_cospi(alpha) +
-              ((alpha == na) ? 0 :
-               bessel_y_ex(x, -alpha, bj) * gnm_sinpi(alpha)));
-    }
-    nb = 1 + (long)na; /* nb-1 <= alpha < nb */
-    alpha -= (gnm_float)(nb-1);
-    J_bessel(&x, &alpha, &nb, bj, &ncalc);
-    if(ncalc != nb) {/* error input */
-      if(ncalc < 0)
-       MATHLIB_WARNING4(("bessel_j(%" GNM_FORMAT_g "): ncalc (=%ld) != nb (=%ld); alpha=%" GNM_FORMAT_g ". 
Arg. out of range?\n"),
-                        x, ncalc, nb, alpha);
-      else
-       MATHLIB_WARNING2(("bessel_j(%" GNM_FORMAT_g ",nu=%" GNM_FORMAT_g "): precision lost in result\n"),
-                        x, alpha+(gnm_float)nb-1);
-    }
-    x = bj[nb-1];
-    return x;
-}
-
-static void J_bessel(gnm_float *x, gnm_float *alpha, long *nb,
-                    gnm_float *b, long *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 gnm_float pi2 = GNM_const(.636619772367581343075535);
-    const static gnm_float twopi1 = GNM_const(6.28125);
-    const static gnm_float twopi2 =  GNM_const(.001935307179586476925286767);
-
-/*---------------------------------------------------------------------
- *  Factorial(N)
- *--------------------------------------------------------------------- */
-/* removed array fact */
-
-    /* Local variables */
-    long nend, intx, nbmx, i, j, k, l, m, n, nstart;
-
-    gnm_float nu, twonu, capp, capq, pold, vcos, test, vsin;
-    gnm_float p, s, t, z, alpem, halfx, aa, bb, cc, psave, plast;
-    gnm_float 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);
-           /* indeed, the limit is 0,
-            * but the cutoff happens too early */
-           for(i=1; i <= *nb; i++)
-               b[i] = 0.; /*was gnm_pinf (really nonsense) */
-           return;
-       }
-       intx = (long) (*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.)    ? gnm_pow(halfx, nu) / (nu * gnm_gamma(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 = gnm_sqrt(pi2 / *x);
-           xin = 1 / (64 * *x * *x);
-           if (*x >= 130.)     m = 4;
-           else if (*x >= 35.) m = 8;
-           else                m = 11;
-           xm = 4. * (gnm_float) m;
-           /* ------------------------------------------------
-              Argument reduction for SIN and COS routines.
-              ------------------------------------------------ */
-           t = gnm_trunc(*x / (twopi1 + twopi2) + .5);
-           z = (*x - t * twopi1) - t * twopi2 - (nu + .5) / pi2;
-           vsin = gnm_sin(z);
-           vcos = gnm_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 / gnm_fact(k);
-               capq = s * t1/ gnm_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. / gnm_fact(k - 2)) * s * t  * xin;
-                   capq = (capq + 1. / gnm_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 = (gnm_float)(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 = (gnm_float) (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 = (gnm_float) (n + n) + twonu;
-               /* -----------------------------------------------------
-                  Calculate special significance test for NBMX > 2.
-                  -----------------------------------------------------*/
-               test = fmax2(test, gnm_sqrt(plast * ensig_BESS) * gnm_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 = (gnm_float)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(gnm_abs(nu) > 1e-15)
-               sum *= (gnm_gamma(nu) * gnm_pow(.5* *x, -nu));
-
-           aa = enmten_BESS;
-           if (sum > 1.)
-               aa *= sum;
-           for (n = 1; n <= *nb; ++n) {
-               if (gnm_abs(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;
-    }
-}
-/* Cleaning up done by tools/import-R:  */
-#undef min0
-
-/* ------------------------------------------------------------------------ */
 /* Imported src/nmath/bessel_k.c from R.  */
 /*
  *  Mathlib : A C Library of Special Functions
@@ -1730,623 +1156,1262 @@ L420:
 }
 
 /* ------------------------------------------------------------------------ */
-/* Imported src/nmath/bessel_y.c from R.  */
-/*
- *  Mathlib : A C Library of Special Functions
- *  Copyright (C) 1998-2012 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
- *  http://www.r-project.org/Licenses/
- */
 
-/*  DESCRIPTION --> see below */
+static gboolean
+bessel_j_series_domain (gnm_float x, gnm_float v)
+{
+       // The taylor series is valid for all possible values of x and v,
+       // but it isn't efficient and practical for all.
+       //
+       // Since bessel_j is used for computing BesselY when v is not an
+       // integer, we need the domain to be independent of the sign of v
+       // for such v.
+       //
+       // That is a bit of a problem because when v < -0.5 and close
+       // to an integer, |v+k| will get close to 0 and the terms will
+       // suddenly jump in size.  The jump will not be larger than a
+       // factor of 2^53, so as long as [-v]! is much larger than that
+       // we do not have a problem.
+
+       if (v < 0 && v == gnm_floor (v))
+               return FALSE;
 
+       // For non-negative v, the factorials will dominate after the
+       // k'th term if x*x/4 < c*k(v+k).  Let's require two bit decreases
+       // (c=0.25) from k=10.  We ignore the sign of v, see above.
 
-/* From http://www.netlib.org/specfun/rybesl   Fortran translated by f2c,...
- *     ------------------------------=#----    Martin Maechler, ETH Zurich
- */
+       return (x * x / 4 < 0.25 * 10 * (gnm_abs (v) + 10));
+}
 
-#ifndef MATHLIB_STANDALONE
-#endif
 
-#define min0(x, y) (((x) <= (y)) ? (x) : (y))
+static gnm_float
+bessel_j_series (gnm_float x, gnm_float v)
+{
+       GnmQuad qv, qxh, qfv, qs, qt;
+       int efv;
+       void *state = gnm_quad_start ();
+       gnm_float e, s;
 
-static void Y_bessel(gnm_float *x, gnm_float *alpha, long *nb,
-                    gnm_float *by, long *ncalc);
+       gnm_quad_init (&qxh, x / 2);
+       gnm_quad_init (&qv, v);
 
-static gnm_float bessel_y(gnm_float x, gnm_float alpha)
-{
-    long nb, ncalc;
-    gnm_float na, *by;
-#ifndef MATHLIB_STANDALONE
-    const void *vmax;
-#endif
+       gnm_quad_pow (&qt, &e, &qxh, &qv);
 
-#ifdef IEEE_754
-    /* NaNs propagated correctly */
-    if (gnm_isnan(x) || gnm_isnan(alpha)) return x + alpha;
-#endif
-    if (x < 0) {
-       ML_ERROR(ME_RANGE);
-       return gnm_nan;
-    }
-    na = gnm_floor(alpha);
-
-    if (alpha != na &&
-       bessel_j_series_domain (x, alpha) &&
-       bessel_j_series_domain (x, -alpha)) {
-           gnm_float s = gnm_sinpi (alpha);
-           gnm_float c = gnm_cospi (alpha);
-           return ((c ? bessel_j_series (x, alpha) * c : 0) - bessel_j_series (x, -alpha)) / s;
-    }
+       switch (qfactf (v, &qfv, &efv)) {
+       case 0:
+               gnm_quad_div (&qt, &qt, &qfv);
+               e -= efv;
+               break;
+       case 1:
+               qt = gnm_quad_zero;
+               e = 0;
+               break;
+       default:
+               gnm_quad_init (&qt, gnm_nan);
+               e = 0;
+               break;
+       }
 
-    if (alpha < 0) {
-       /* Using Abramowitz & Stegun  9.1.2
-        * this may not be quite optimal (CPU and accuracy wise) */
-       return(bessel_y(x, -alpha) * gnm_cospi(alpha) -
-              ((alpha == na) ? 0 :
-               bessel_j(x, -alpha) * gnm_sinpi(alpha)));
-    }
-    nb = 1+ (long)na;/* nb-1 <= alpha < nb */
-    alpha -= (gnm_float)(nb-1);
-#ifdef MATHLIB_STANDALONE
-    by = (gnm_float *) calloc(nb, sizeof(gnm_float));
-    if (!by) MATHLIB_ERROR("%s", ("bessel_y allocation error"));
-#else
-    vmax = vmaxget();
-    by = (gnm_float *) R_alloc((size_t) nb, sizeof(gnm_float));
-#endif
-    Y_bessel(&x, &alpha, &nb, by, &ncalc);
-    if(ncalc != nb) {/* error input */
-       if(ncalc == -1) {
-            free(by);
-           return gnm_pinf;
-       } else if(ncalc < -1)
-           MATHLIB_WARNING4(("bessel_y(%" GNM_FORMAT_g "): ncalc (=%ld) != nb (=%ld); alpha=%" GNM_FORMAT_g 
". Arg. out of range?\n"),
-                            x, ncalc, nb, alpha);
-       else /* ncalc >= 0 */
-           MATHLIB_WARNING2(("bessel_y(%" GNM_FORMAT_g ",nu=%" GNM_FORMAT_g "): precision lost in result\n"),
-                            x, alpha+(gnm_float)nb-1);
-    }
-    x = by[nb-1];
-#ifdef MATHLIB_STANDALONE
-    free(by);
-#else
-    vmaxset(vmax);
-#endif
-    return x;
+       // Clamp won't affect whether we get 0 or inf.
+       e = CLAMP (e, G_MININT, G_MAXINT);
+       qt.h = gnm_ldexp (qt.h, (int)e);
+       qt.l = gnm_ldexp (qt.l, (int)e);
+
+       qs = qt;
+       s = gnm_quad_value (&qs);
+       if (gnm_finite (s) && s != 0) {
+               int k, mink = 5;
+               GnmQuad qxh2;
+
+               gnm_quad_mul (&qxh2, &qxh, &qxh);
+
+               if (v < 0) {
+                       // Terms can get suddenly big for k ~ -v.
+                       gnm_float ltn0 = -v * (1 - M_LN2gnum + gnm_log (x / -v));
+                       if (ltn0 - gnm_log (s) < gnm_log (GNM_EPSILON) - 10)
+                               mink = (int)(-v) + 5;
+               }
+
+               for (k = 1; k < 200; k++) {
+                       GnmQuad qa, qb;
+                       gnm_float t;
+
+                       gnm_quad_mul (&qt, &qt, &qxh2);
+                       gnm_quad_init (&qa, -k);
+                       gnm_quad_sub (&qb, &qv, &qa);
+                       gnm_quad_mul (&qa, &qa, &qb);
+                       gnm_quad_div (&qt, &qt, &qa);
+                       t = gnm_quad_value (&qt);
+                       if (t == 0)
+                               break;
+                       gnm_quad_add (&qs, &qs, &qt);
+                       s = gnm_quad_value (&qs);
+                       if (k >= mink &&
+                           gnm_abs (t) <= GNM_EPSILON / 1024 * gnm_abs (s))
+                               break;
+               }
+       }
+
+       gnm_quad_end (state);
+
+       return s;
 }
 
-/* modified version of bessel_y that accepts a work array instead of
-   allocating one. */
-static gnm_float bessel_y_ex(gnm_float x, gnm_float alpha, gnm_float *by)
+/* ------------------------------------------------------------------------ */
+
+static const gnm_float legendre20_roots[(20 + 1) / 2] = {
+       GNM_const(0.0765265211334973),
+       GNM_const(0.2277858511416451),
+       GNM_const(0.3737060887154195),
+       GNM_const(0.5108670019508271),
+       GNM_const(0.6360536807265150),
+       GNM_const(0.7463319064601508),
+       GNM_const(0.8391169718222188),
+       GNM_const(0.9122344282513259),
+       GNM_const(0.9639719272779138),
+       GNM_const(0.9931285991850949)
+};
+
+static const gnm_float legendre20_wts[(20 + 1) / 2] = {
+       GNM_const(0.1527533871307258),
+       GNM_const(0.1491729864726037),
+       GNM_const(0.1420961093183820),
+       GNM_const(0.1316886384491766),
+       GNM_const(0.1181945319615184),
+       GNM_const(0.1019301198172404),
+       GNM_const(0.0832767415767048),
+       GNM_const(0.0626720483341091),
+       GNM_const(0.0406014298003869),
+       GNM_const(0.0176140071391521)
+};
+
+static const gnm_float legendre33_roots[(33 + 1) / 2] = {
+       GNM_const(0.0000000000000000),
+       GNM_const(0.0936310658547334),
+       GNM_const(0.1864392988279916),
+       GNM_const(0.2776090971524970),
+       GNM_const(0.3663392577480734),
+       GNM_const(0.4518500172724507),
+       GNM_const(0.5333899047863476),
+       GNM_const(0.6102423458363790),
+       GNM_const(0.6817319599697428),
+       GNM_const(0.7472304964495622),
+       GNM_const(0.8061623562741665),
+       GNM_const(0.8580096526765041),
+       GNM_const(0.9023167677434336),
+       GNM_const(0.9386943726111684),
+       GNM_const(0.9668229096899927),
+       GNM_const(0.9864557262306425),
+       GNM_const(0.9974246942464552)
+};
+
+static const gnm_float legendre33_wts[(33 + 1) / 2] = {
+       GNM_const(0.0937684461602100),
+       GNM_const(0.0933564260655961),
+       GNM_const(0.0921239866433168),
+       GNM_const(0.0900819586606386),
+       GNM_const(0.0872482876188443),
+       GNM_const(0.0836478760670387),
+       GNM_const(0.0793123647948867),
+       GNM_const(0.0742798548439541),
+       GNM_const(0.0685945728186567),
+       GNM_const(0.0623064825303175),
+       GNM_const(0.0554708466316636),
+       GNM_const(0.0481477428187117),
+       GNM_const(0.0404015413316696),
+       GNM_const(0.0323003586323290),
+       GNM_const(0.0239155481017495),
+       GNM_const(0.0153217015129347),
+       GNM_const(0.0066062278475874)
+};
+
+static const gnm_float legendre45_roots[(45 + 1) / 2] = {
+       GNM_const(0.0000000000000000),
+       GNM_const(0.0689869801631442),
+       GNM_const(0.1376452059832530),
+       GNM_const(0.2056474897832637),
+       GNM_const(0.2726697697523776),
+       GNM_const(0.3383926542506022),
+       GNM_const(0.4025029438585419),
+       GNM_const(0.4646951239196351),
+       GNM_const(0.5246728204629161),
+       GNM_const(0.5821502125693532),
+       GNM_const(0.6368533944532233),
+       GNM_const(0.6885216807712006),
+       GNM_const(0.7369088489454904),
+       GNM_const(0.7817843125939062),
+       GNM_const(0.8229342205020863),
+       GNM_const(0.8601624759606642),
+       GNM_const(0.8932916717532418),
+       GNM_const(0.9221639367190004),
+       GNM_const(0.9466416909956291),
+       GNM_const(0.9666083103968947),
+       GNM_const(0.9819687150345405),
+       GNM_const(0.9926499984472037),
+       GNM_const(0.9986036451819367)
+};
+
+static const gnm_float legendre45_wts[(45 + 1) / 2] = {
+       GNM_const(0.0690418248292320),
+       GNM_const(0.0688773169776613),
+       GNM_const(0.0683845773786697),
+       GNM_const(0.0675659541636075),
+       GNM_const(0.0664253484498425),
+       GNM_const(0.0649681957507234),
+       GNM_const(0.0632014400738199),
+       GNM_const(0.0611335008310665),
+       GNM_const(0.0587742327188417),
+       GNM_const(0.0561348787597865),
+       GNM_const(0.0532280167312690),
+       GNM_const(0.0500674992379520),
+       GNM_const(0.0466683877183734),
+       GNM_const(0.0430468807091650),
+       GNM_const(0.0392202367293025),
+       GNM_const(0.0352066922016090),
+       GNM_const(0.0310253749345155),
+       GNM_const(0.0266962139675777),
+       GNM_const(0.0222398475505787),
+       GNM_const(0.0176775352579376),
+       GNM_const(0.0130311049915828),
+       GNM_const(0.0083231892962182),
+       GNM_const(0.0035826631552836)
+};
+
+typedef void (*ComplexIntegrand) (gnm_complex *res, gnm_float x,
+                                 const gnm_float *args);
+
+static void
+complex_legendre_integral (gnm_complex *res, size_t N,
+                          gnm_float L, gnm_float H,
+                          ComplexIntegrand f, const gnm_float *args)
 {
-    long nb, ncalc;
-    gnm_float na;
+       const gnm_float *roots;
+       const gnm_float *wts;
+       gnm_float m = (L + H) / 2;
+       gnm_float s = (H - L) / 2;
+       size_t i;
+
+       switch (N) {
+       case 20:
+               roots = legendre20_roots;
+               wts = legendre20_wts;
+               break;
+       case 33:
+               roots = legendre33_roots;
+               wts = legendre33_wts;
+               break;
+       case 45:
+               roots = legendre45_roots;
+               wts = legendre45_wts;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+       if (N & 1)
+               g_assert (roots[0] == 0.0);
+
+       gnm_complex_init (res, 0, 0);
+       for (i = 0; i < (N + 1) / 2; i++) {
+               gnm_float r = roots[i];
+               gnm_float w = wts[i];
+               int neg;
+               for (neg = 0; neg <= 1; neg++) {
+                       gnm_float u = neg ? m - s * r : m + s * r;
+                       gnm_complex dI;
+                       f (&dI, u, args);
+                       gnm_complex_scale_real (&dI, w);
+                       gnm_complex_add (res, res, &dI);
+                       if (i == 0 && (N & 1))
+                               break;
+               }
+       }
+       res->re *= s;
+       res->im *= s;
+}
 
-#ifdef IEEE_754
-    /* NaNs propagated correctly */
-    if (gnm_isnan(x) || gnm_isnan(alpha)) return x + alpha;
-#endif
-    if (x < 0) {
-       ML_ERROR(ME_RANGE);
-       return gnm_nan;
-    }
-    na = gnm_floor(alpha);
-    if (alpha < 0) {
-       /* Using Abramowitz & Stegun  9.1.2
-        * this may not be quite optimal (CPU and accuracy wise) */
-       return(bessel_y_ex(x, -alpha, by) * gnm_cospi(alpha) -
-              ((alpha == na) ? 0 :
-               bessel_j_ex(x, -alpha, by) * gnm_sinpi(alpha)));
-    }
-    nb = 1+ (long)na;/* nb-1 <= alpha < nb */
-    alpha -= (gnm_float)(nb-1);
-    Y_bessel(&x, &alpha, &nb, by, &ncalc);
-    if(ncalc != nb) {/* error input */
-       if(ncalc == -1)
-           return gnm_pinf;
-       else if(ncalc < -1)
-           MATHLIB_WARNING4(("bessel_y(%" GNM_FORMAT_g "): ncalc (=%ld) != nb (=%ld); alpha=%" GNM_FORMAT_g 
". Arg. out of range?\n"),
-                            x, ncalc, nb, alpha);
-       else /* ncalc >= 0 */
-           MATHLIB_WARNING2(("bessel_y(%" GNM_FORMAT_g ",nu=%" GNM_FORMAT_g "): precision lost in result\n"),
-                            x, alpha+(gnm_float)nb-1);
-    }
-    x = by[nb-1];
-    return x;
+// Trapezoid rule integraion for a complex function defined on a finite
+// interval.  This breaks the interval into N uniform pieces.
+static void
+complex_trapezoid_integral (gnm_complex *res, size_t N,
+                           gnm_float L, gnm_float H,
+                           ComplexIntegrand f, const gnm_float *args)
+{
+       gnm_float s = (H - L) / N;
+       size_t i;
+
+       gnm_complex_init (res, 0, 0);
+       for (i = 0; i <= N; i++) {
+               gnm_float u = L + i * s;
+               gnm_complex dI;
+               f (&dI, u, args);
+               if (i == 0 || i == N) {
+                       dI.re /= 2;
+                       dI.im /= 2;
+               }
+               gnm_complex_add (res, res, &dI);
+       }
+       res->re *= s;
+       res->im *= s;
 }
 
-static void Y_bessel(gnm_float *x, gnm_float *alpha, long *nb,
-                    gnm_float *by, long *ncalc)
+// Shrink integration range to exclude vanishing outer parts.
+static void
+complex_shink_integral_range (gnm_float *L, gnm_float *H, gnm_float refx,
+                             ComplexIntegrand f,
+                             const gnm_float *args)
 {
-/* ----------------------------------------------------------------------
+       gnm_complex y;
+       gnm_float refy, limL = refx, limH = refx;
+       gboolean first;
+       gboolean debug = FALSE;
 
- This routine calculates Bessel functions Y_(N+ALPHA) (X)
- for non-negative argument X, and non-negative order N+ALPHA.
+       g_return_if_fail (*L <= *H);
+       g_return_if_fail (*L <= refx && refx <= *H);
 
+       f (&y, refx, args);
+       refy = gnm_complex_mod (&y) * GNM_EPSILON;
 
- Explanation of variables in the calling sequence
+       g_return_if_fail (!gnm_isnan (refy));
 
- X     - Non-negative argument for which
-        Y's are to be calculated.
- ALPHA - Fractional part of order for which
-        Y's are to be calculated.  0 <= ALPHA < 1.0.
- NB    - Number of functions to be calculated, NB > 0.
-        The first function calculated is of order ALPHA, and the
-        last is of order (NB - 1 + ALPHA).
- BY    - Output vector of length NB.   If the
-        routine terminates normally (NCALC=NB), the vector BY
-        contains the functions Y(ALPHA,X), ... , Y(NB-1+ALPHA,X),
-        If (0 < NCALC < NB), BY(I) contains correct function
-        values for I <= NCALC, and contains the ratios
-        Y(ALPHA+I-1,X)/Y(ALPHA+I-2,X) for the rest of the array.
- NCALC - Output variable indicating possible errors.
-        Before using the vector BY, the user should check that
-        NCALC=NB, i.e., all orders have been calculated to
-        the desired accuracy.  See error returns below.
+       if (debug)
+               g_printerr ("Initial range: (%g,%g)  refx=%g  refy=%g\n",
+                           *L, *H, refx, refy);
 
+       first = TRUE;
+       while (limL - *L > GNM_EPSILON) {
+               gnm_float testx = first ? *L : (limL + *L) / 2;
+               gnm_float testy;
 
- *******************************************************************
+               f (&y, testx, args);
+               testy = gnm_complex_mod (&y);
 
- Error returns
+               first = FALSE;
+               if (testy <= refy) {
+                       *L = testx;
+                       if (testy >= refy / 16)
+                               break;
+                       continue;
+               } else
+                       limL = testx;
+       }
 
-  In case of an error, NCALC != NB, and not all Y's are
-  calculated to the desired accuracy.
+       first = TRUE;
+       while (*H - limH > GNM_EPSILON) {
+               gnm_float testx = first ? *H : (*H + limH) / 2;
+               gnm_float testy;
 
-  NCALC < -1:  An argument is out of range. For example,
-       NB <= 0, IZE is not 1 or 2, or IZE=1 and ABS(X) >=
-       XMAX.  In this case, BY[0] = 0.0, the remainder of the
-       BY-vector is not calculated, and NCALC is set to
-       MIN0(NB,0)-2  so that NCALC != NB.
-  NCALC = -1:  Y(ALPHA,X) >= XINF.  The requested function
-       values are set to 0.0.
-  1 < NCALC < NB: Not all requested function values could
-       be calculated accurately.  BY(I) contains correct function
-       values for I <= NCALC, and the remaining NB-NCALC
-       array elements contain 0.0.
+               f (&y, testx, args);
+               testy = gnm_complex_mod (&y);
 
+               first = FALSE;
+               if (testy <= refy) {
+                       *H = testx;
+                       if (testy >= refy / 16)
+                               break;
+                       continue;
+               } else
+                       limH = testx;
+       }
 
- Intrinsic functions required are:
+       if (debug)
+               g_printerr ("Shrunk range: (%g,%g)\n", *L, *H);
+}
 
-     DBLE, EXP, INT, MAX, MIN, REAL, SQRT
+typedef gnm_float (*Interpolant) (gnm_float x, const gnm_float *args);
 
+static gnm_float
+chebyshev_interpolant (size_t N, gnm_float L, gnm_float H, gnm_float x0,
+                      Interpolant f, const gnm_float *args)
+{
+       size_t i, j, k;
+       gnm_float *coeffs = g_new (gnm_float, N);
+       gnm_float m = (L + H) / 2;
+       gnm_float s = (H - L) / 2;
+       gnm_float x0n = (x0 - m) / s;
+       gnm_float res, dip1, dip2, di;
+
+       for (j = 0; j < N; j++) {
+               gnm_float cj = 0;
+               for (k = 0; k < N; k++) {
+                       gnm_float zj = gnm_cospi ((k + 0.5) / N);
+                       gnm_float xj = m + s * zj;
+                       gnm_float fxj = f (xj, args);
+                       cj += fxj * gnm_cospi (j * (k + 0.5) / N);
+               }
+               coeffs[j] = cj * 2.0 / N;
+       }
 
- Acknowledgement
+       dip1 = 0.0;
+       di = 0.0;
 
-       This program draws heavily on Temme's Algol program for Y(a,x)
-       and Y(a+1,x) and on Campbell's programs for Y_nu(x).    Temme's
-       scheme is used for  x < THRESH, and Campbell's scheme is used
-       in the asymptotic region.  Segments of code from both sources
-       have been translated into Fortran 77, merged, and heavily modified.
-       Modifications include parameterization of machine dependencies,
-       use of a new approximation for ln(gamma(x)), and built-in
-       protection against over/underflow.
+       for (i = N - 1; i >= 1; i--) {
+               dip2 = dip1;
+               dip1 = di;
+               di = 2.0 * x0n * dip1 - dip2 + coeffs[i];
+       }
+       res = x0n * di - dip1 + 0.5 * coeffs[0];
 
- References: "Bessel functions J_nu(x) and Y_nu(x) of float
-             order and float argument," Campbell, J. B.,
-             Comp. Phy. Comm. 18, 1979, pp. 133-142.
+       g_free (coeffs);
 
-            "On the numerical evaluation of the ordinary
-             Bessel function of the second kind," Temme,
-             N. M., J. Comput. Phys. 21, 1976, pp. 343-350.
+       if (0) g_printerr ("--> f(%g) = %.20g\n", x0, res);
 
-  Latest modification: March 19, 1990
+       return res;
+}
 
-  Modified by: W. J. Cody
-              Applied Mathematics Division
-              Argonne National Laboratory
-              Argonne, IL  60439
- ----------------------------------------------------------------------*/
-
-
-/* ----------------------------------------------------------------------
-  Mathematical constants
-    FIVPI = 5*PI
-    PIM5 = 5*PI - 15
- ----------------------------------------------------------------------*/
-    const static gnm_float fivpi = GNM_const(15.707963267948966192);
-    const static gnm_float pim5        =   GNM_const(.70796326794896619231);
-
-    /*----------------------------------------------------------------------
-      Coefficients for Chebyshev polynomial expansion of
-      1/gamma(1-x), abs(x) <= .5
-      ----------------------------------------------------------------------*/
-    const static gnm_float ch[21] = { GNM_const(-6.7735241822398840964e-24),
-           GNM_const(-6.1455180116049879894e-23),GNM_const(2.9017595056104745456e-21),
-           GNM_const(1.3639417919073099464e-19),GNM_const(2.3826220476859635824e-18),
-           GNM_const(-9.0642907957550702534e-18),GNM_const(-1.4943667065169001769e-15),
-           GNM_const(-3.3919078305362211264e-14),GNM_const(-1.7023776642512729175e-13),
-           GNM_const(9.1609750938768647911e-12),GNM_const(2.4230957900482704055e-10),
-           GNM_const(1.7451364971382984243e-9),GNM_const(-3.3126119768180852711e-8),
-           GNM_const(-8.6592079961391259661e-7),GNM_const(-4.9717367041957398581e-6),
-           GNM_const(7.6309597585908126618e-5),GNM_const(.0012719271366545622927),
-           GNM_const(.0017063050710955562222),GNM_const(-.07685284084478667369),
-           GNM_const(-.28387654227602353814),GNM_const(.92187029365045265648) };
 
-    /* Local variables */
-    long i, k, na;
+// Coefficient for the debye polynomial u_n
+// Lowest coefficent will be for x^n
+// Highest coefficent will be for x^(3n)
+// Every second coefficent is zero and left out.
+// The count of coefficents will thus be n+1.
+static const gnm_float *
+debye_u_coeffs (size_t n)
+{
+       static gnm_float **coeffs = NULL;
+       static size_t nalloc = 0;
+
+       if (n >= nalloc) {
+               size_t i;
+               coeffs = g_renew (gnm_float *, coeffs, n + 1);
+               for (i = nalloc; i <= n; i++) {
+                       gnm_float *c = coeffs[i] = g_new0 (gnm_float, i + 1);
+                       gnm_float *l;
+                       size_t j;
+
+                       if (i == 0) {
+                               c[0] = 1;
+                               continue;
+                       } else if (i == 1) {
+                               c[0] = 1 / 8.0;
+                               c[1] = -5 / 24.0;
+                               continue;
+                       }
 
-    gnm_float alfa, div, ddiv, even, gamma, term, cosmu, sinmu,
-       b, c, d, e, f, g, h, p, q, r, s, d1, d2, q0, pa,pa1, qa,qa1,
-       en, en1, nu, ex,  ya,ya1, twobyx, den, odd, aye, dmu, x2, xna;
+                       l = coeffs[i - 1];
 
-    en1 = ya = ya1 = 0;                /* -Wall */
+                       for (j = i; j <= 3 * i; j += 2) {
+                               gnm_float k = 0;
 
-    ex = *x;
-    nu = *alpha;
-    if (*nb > 0 && 0. <= nu && nu < 1.) {
-       if(ex < GNM_MIN || ex > xlrg_BESS_Y) {
-           /* Warning is not really appropriate, give
-            * proper limit:
-            * ML_ERROR(ME_RANGE); */
-           *ncalc = *nb;
-           if(ex > xlrg_BESS_Y)  by[0]= 0.; /*was gnm_pinf */
-           else if(ex < GNM_MIN) by[0]=gnm_ninf;
-           for(i=0; i < *nb; i++)
-               by[i] = by[0];
-           return;
+                               // .5tt u_[i-1]'
+                               if (j < 3 * i)
+                                       k += 0.5 * (j-1) * l[((j-1)-(i-1)) / 2];
+
+                               // -.5tttt u[i-1]'
+                               if (j > i)
+                                       k -= 0.5 * (j-3) * l[((j-3)-(i-1)) / 2];
+
+                               // 1/8*Int[u[i-1]]
+                               if (j < 3 * i)
+                                       k += 0.125 * l[((j-1)-(i-1)) / 2] / j;
+
+
+                               // -5/8*Int[tt*u[i-1]]
+                               if (j > i)
+                                       k -= 0.625 * l[((j-3)-(i-1)) / 2] / j;
+
+                               c[(j - i) / 2] = k;
+
+                               if (0)
+                                       g_printerr ("Debye u_%d : %g * x^%d\n",
+                                                   (int)i, k, (int)j);
+                       }
+               }
+               nalloc = n + 1;
        }
-       xna = gnm_trunc(nu + .5);
-       na = (long) xna;
-       if (na == 1) {/* <==>  .5 <= *alpha < 1  <==>  -5. <= nu < 0 */
-           nu -= xna;
+
+       return coeffs[n];
+}
+
+static void
+debye_u (gnm_complex *res, size_t n, gnm_float p, gboolean qip)
+{
+       const gnm_float *coeffs = debye_u_coeffs (n);
+       gnm_float pn = gnm_pow (p, n);
+       gnm_float pp = qip ? -p * p : p * p;
+       gnm_float s = 0;
+       int i;
+
+       for (i = 3 * n; i >= (int)n; i -= 2)
+               s = s * pp + coeffs[(i - n) / 2];
+
+       switch (qip ? n % 4 : 0) {
+       case 0:
+               gnm_complex_init (res, s * pn, 0);
+               break;
+       case 1:
+               gnm_complex_init (res, 0, s * pn);
+               break;
+       case 2:
+               gnm_complex_init (res, s * -pn, 0);
+               break;
+       case 3:
+               gnm_complex_init (res, 0, s * -pn);
+               break;
+       default:
+               g_assert_not_reached ();
        }
-       if (nu == -.5) {
-           p = M_SQRT_2dPI / gnm_sqrt(ex);
-           ya = p * gnm_sin(ex);
-           ya1 = -p * gnm_cos(ex);
-       } else if (ex < 3.) {
-           /* -------------------------------------------------------------
-              Use Temme's scheme for small X
-              ------------------------------------------------------------- */
-           b = ex * .5;
-           d = -gnm_log(b);
-           f = nu * d;
-           e = gnm_pow(b, -nu);
-           if (gnm_abs(nu) < M_eps_sinc)
-               c = M_1_PI;
-           else
-               c = nu / gnm_sin(nu * M_PIgnum);
+}
 
-           /* ------------------------------------------------------------
-              Computation of gnm_sinh(f)/f
-              ------------------------------------------------------------ */
-           if (gnm_abs(f) < 1.) {
-               x2 = f * f;
-               en = 19.;
-               s = 1.;
-               for (i = 1; i <= 9; ++i) {
-                   s = s * x2 / en / (en - 1.) + 1.;
-                   en -= 2.;
+
+static gnm_float
+gnm_sinv_m_v_cosv (gnm_float v, gnm_float sinv)
+{
+       // Deviation: Matviyenko uses direct formula for this for all v.
+
+       if (v >= 1)
+               return sinv - v * gnm_cos (v);
+       else {
+               gnm_float r = 0, t = -v;
+               gnm_float vv = v * v;
+               int i;
+
+               for (i = 3; i < 100; i += 2) {
+                       t = -t * vv / (i * (i == 3 ? 1 : i - 3));
+                       r += t;
+                       if (gnm_abs (t) <= gnm_abs (r) * (GNM_EPSILON / 16))
+                               break;
                }
-           } else {
-               s = (e - GNM_const(1.) / e) * .5 / f;
-           }
-           /* --------------------------------------------------------
-              Computation of 1/gamma(1-a) using Chebyshev polynomials */
-           x2 = nu * nu * 8.;
-           aye = ch[0];
-           even = 0.;
-           alfa = ch[1];
-           odd = 0.;
-           for (i = 3; i <= 19; i += 2) {
-               even = -(aye + aye + even);
-               aye = -even * x2 - aye + ch[i - 1];
-               odd = -(alfa + alfa + odd);
-               alfa = -odd * x2 - alfa + ch[i];
-           }
-           even = (even * .5 + aye) * x2 - aye + ch[20];
-           odd = (odd + alfa) * 2.;
-           gamma = odd * nu + even;
-           /* End of computation of 1/gamma(1-a)
-              ----------------------------------------------------------- */
-           g = e * gamma;
-           e = (e + GNM_const(1.) / e) * .5;
-           f = 2. * c * (odd * e + even * s * d);
-           e = nu * nu;
-           p = g * c;
-           q = M_1_PI / g;
-           c = nu * M_PI_2gnum;
-           if (gnm_abs(c) < M_eps_sinc)
-               r = 1.;
-           else
-               r = gnm_sin(c) / c;
-
-           r = M_PIgnum * c * r * r;
-           c = 1.;
-           d = -b * b;
-           h = 0.;
-           ya = f + r * q;
-           ya1 = p;
-           en = 1.;
-
-           while (gnm_abs(g / (1. + gnm_abs(ya))) +
-                  gnm_abs(h / (1. + gnm_abs(ya1))) > GNM_EPSILON) {
-               f = (f * en + p + q) / (en * en - e);
-               c *= (d / en);
-               p /= en - nu;
-               q /= en + nu;
-               g = c * (f + r * q);
-               h = c * p - en * g;
-               ya += g;
-               ya1+= h;
-               en += 1.;
-           }
-           ya = -ya;
-           ya1 = -ya1 / b;
-       } else if (ex < thresh_BESS_Y) {
-           /* --------------------------------------------------------------
-              Use Temme's scheme for moderate X :  3 <= x < 16
-              -------------------------------------------------------------- */
-           c = (.5 - nu) * (.5 + nu);
-           b = ex + ex;
-           e = ex * M_1_PI * gnm_cos(nu * M_PIgnum) / GNM_EPSILON;
-           e *= e;
-           p = 1.;
-           q = -ex;
-           r = 1. + ex * ex;
-           s = r;
-           en = 2.;
-           while (r * en * en < e) {
-               en1 = en + 1.;
-               d = (en - 1. + c / en) / s;
-               p = (en + en - p * d) / en1;
-               q = (-b + q * d) / en1;
-               s = p * p + q * q;
-               r *= s;
-               en = en1;
-           }
-           f = p / s;
-           p = f;
-           g = -q / s;
-           q = g;
-L220:
-           en -= 1.;
-           if (en > 0.) {
-               r = en1 * (2. - p) - 2.;
-               s = b + en1 * q;
-               d = (en - 1. + c / en) / (r * r + s * s);
-               p = d * r;
-               q = d * s;
-               e = f + 1.;
-               f = p * e - g * q;
-               g = q * e + p * g;
-               en1 = en;
-               goto L220;
-           }
-           f = 1. + f;
-           d = f * f + g * g;
-           pa = f / d;
-           qa = -g / d;
-           d = nu + .5 - p;
-           q += ex;
-           pa1 = (pa * q - qa * d) / ex;
-           qa1 = (qa * q + pa * d) / ex;
-           b = ex - M_PI_2gnum * (nu + .5);
-           c = gnm_cos(b);
-           s = gnm_sin(b);
-           d = M_SQRT_2dPI / gnm_sqrt(ex);
-           ya = d * (pa * s + qa * c);
-           ya1 = d * (qa1 * s - pa1 * c);
-       } else { /* x > thresh_BESS_Y */
-           /* ----------------------------------------------------------
-              Use Campbell's asymptotic scheme.
-              ---------------------------------------------------------- */
-           na = 0;
-           d1 = gnm_trunc(ex / fivpi);
-           i = (long) d1;
-           dmu = ex - 15. * d1 - d1 * pim5 - (*alpha + .5) * M_PI_2gnum;
-           if (i - (i / 2 << 1) == 0) {
-               cosmu = gnm_cos(dmu);
-               sinmu = gnm_sin(dmu);
-           } else {
-               cosmu = -gnm_cos(dmu);
-               sinmu = -gnm_sin(dmu);
-           }
-           ddiv = 8. * ex;
-           dmu = *alpha;
-           den = gnm_sqrt(ex);
-           for (k = 1; k <= 2; ++k) {
-               p = cosmu;
-               cosmu = sinmu;
-               sinmu = -p;
-               d1 = (2. * dmu - 1.) * (2. * dmu + 1.);
-               d2 = 0.;
-               div = ddiv;
-               p = 0.;
-               q = 0.;
-               q0 = d1 / div;
-               term = q0;
-               for (i = 2; i <= 20; ++i) {
-                   d2 += 8.;
-                   d1 -= d2;
-                   div += ddiv;
-                   term = -term * d1 / div;
-                   p += term;
-                   d2 += 8.;
-                   d1 -= d2;
-                   div += ddiv;
-                   term *= (d1 / div);
-                   q += term;
-                   if (gnm_abs(term) <= GNM_EPSILON) {
-                       break;
-                   }
+
+               if (0) {
+                       gnm_float ref = sinv - v * gnm_cos (v);
+                       g_printerr ("XXX: %g %g %g -- %g\n",
+                                   v, ref, r, r - ref);
                }
-               p += 1.;
-               q += q0;
-               if (k == 1)
-                   ya = M_SQRT_2dPI * (p * cosmu - q * sinmu) / den;
-               else
-                   ya1 = M_SQRT_2dPI * (p * cosmu - q * sinmu) / den;
-               dmu += 1.;
-           }
+
+               return r;
        }
-       if (na == 1) {
-           h = 2. * (nu + 1.) / ex;
-           if (h > 1.) {
-               if (gnm_abs(ya1) > GNM_MAX / h) {
-                   h = 0.;
-                   ya = 0.;
+}
+
+static gnm_float
+gnm_sinhumu (gnm_float u)
+{
+       if (!gnm_finite (u))
+               return u;
+       else if (gnm_abs (u) >= 1)
+               return gnm_sinh (u) - u;
+       else {
+               gnm_float uu = u * u;
+               size_t i;
+               gnm_float s = 0;
+               gnm_float t = u;
+
+               for (i = 3; i < 100; i += 2) {
+                       t *= uu / ((i - 1) * i);
+                       s += t;
+                       if (gnm_abs (t) <= gnm_abs (s) * (GNM_EPSILON / 16))
+                               break;
                }
-           }
-           h = h * ya1 - ya;
-           ya = ya1;
-           ya1 = h;
+               return s;
        }
+}
 
-       /* ---------------------------------------------------------------
-          Now have first one or two Y's
-          --------------------------------------------------------------- */
-       by[0] = ya;
-       *ncalc = 1;
-       if(*nb > 1) {
-           by[1] = ya1;
-           if (ya1 != 0.) {
-               aye = 1. + *alpha;
-               twobyx = GNM_const(2.) / ex;
-               *ncalc = 2;
-               for (i = 2; i < *nb; ++i) {
-                   if (twobyx < 1.) {
-                       if (gnm_abs(by[i - 1]) * twobyx >= GNM_MAX / aye)
-                           goto L450;
-                   } else {
-                       if (gnm_abs(by[i - 1]) >= GNM_MAX / aye / twobyx)
-                           goto L450;
-                   }
-                   by[i] = twobyx * aye * by[i - 1] - by[i - 2];
-                   aye += 1.;
-                   ++(*ncalc);
+/* ------------------------------------------------------------------------ */
+
+static void
+debye_u_sum (gnm_complex *res, gnm_float x, gnm_float nu,
+            size_t N, gboolean qalt, gboolean qip)
+{
+       size_t n;
+       gnm_float f;
+       gnm_float sqdiff = gnm_abs (x * x - nu * nu);
+       gnm_float diff2 = gnm_sqrt (sqdiff);
+       gnm_float p = nu / diff2;
+
+       (void)debye_u_coeffs (N);
+
+       gnm_complex_init (res, 0, 0);
+       f = 1;
+       for (n = 0; n <= N; n++) {
+               gnm_complex t;
+               if (nu == 0) {
+                       // lim(p/nu,nu->0) = 1/x
+                       gnm_float q = debye_u_coeffs (n)[0] / gnm_pow (x, n);
+                       if (qip && (n & 2)) q = -q;
+                       if (qalt && (n & 1)) q = -q;
+                       if (qip && (n & 1))
+                               gnm_complex_init (&t, 0, q);
+                       else
+                               gnm_complex_init (&t, q, 0);
+               } else {
+                       debye_u (&t, n, p, qip);
+                       gnm_complex_scale_real (&t, f);
+                       f /= nu;
+                       if (qalt) f = -f;
                }
-           }
+               gnm_complex_add (res, res, &t);
        }
-L450:
-       for (i = *ncalc; i < *nb; ++i)
-           by[i] = gnm_ninf;/* was 0 */
+}
 
-    } else {
-       by[0] = 0.;
-       *ncalc = min0(*nb,0) - 1;
-    }
+static void
+debye_29_eta1 (gnm_float x, gnm_float nu,
+              gnm_float *r1a, gnm_float *r1b, gnm_float *rpi)
+{
+       static const gnm_float c[] = {
+               /*  2 */ 1 / (gnm_float)2,
+               /*  4 */ 1 / (gnm_float)24,
+               /*  6 */ 1 / (gnm_float)80,
+               /*  8 */ 5 / (gnm_float)896,
+               /* 10 */ 7 / (gnm_float)2304,
+               /* 12 */ 21 / (gnm_float)11264,
+               /* 14 */ 33 / (gnm_float)26624,
+               /* 16 */ 143 / (gnm_float)163840,
+               /* 18 */ 715 / (gnm_float)1114112,
+               /* 20 */ 2431 / (gnm_float)4980736
+       };
+
+       gnm_float q = nu / x;
+
+       // Deviation: we improve this formula for small nu / x by computing
+       // eta1 in three parts such that eta1 = (*r1a + *r1b + Pi * *rpi)
+       // with *r1a small and no rounding errors on the others.
+
+       if (q < 0.1) {
+               gnm_float r  = 0;
+               gnm_float qq = q * q;
+               unsigned ci;
+               *rpi = -nu / 2 - 0.25;
+
+               for (ci = G_N_ELEMENTS(c); ci-- > 0; )
+                       r = r * qq + c[ci];
+               *r1a = r * q * nu;
+               *r1b = x;
+       } else {
+               *r1a = gnm_sqrt (x * x - nu * nu) - nu * gnm_acos (nu / x);
+               *r1b = 0;
+               *rpi = -0.25;
+       }
 }
 
-/* Cleaning up done by tools/import-R:  */
-#undef min0
+static void
+debye_29 (gnm_complex *res, gnm_float x, gnm_float nu, size_t N)
+{
+       gnm_float sqdiff = x * x - nu * nu;
+       gnm_float eta1a, eta1b, eta1pi;
+       gnm_float f1 = gnm_sqrt (2 / M_PIgnum) / gnm_pow (sqdiff, 0.25);
+       gnm_complex sum, f12, f3;
 
-/* ------------------------------------------------------------------------ */
+       debye_29_eta1 (x, nu, &eta1a, &eta1b, &eta1pi);
 
-static gboolean
-bessel_j_series_domain (gnm_float x, gnm_float v)
-{
-       /*
-        * The series is valid for all possible values of x and v,
-        * but it isn't efficient for all.
-        */
-
-       if (v <= -0.999) {
-               /*
-                * For negative v we must ensure that nothing crazy
-                * happens when |v+k| reaches its minimum.
-                */
-               gnm_float rv = gnm_floor (v + 0.5);
-               if (gnm_abs (v - rv) * v * v < 1)
-                       return FALSE;
-
-               /*
-                * The above condition ensure that at most one term
-                * increases relative to the prior term and that the
-                * next term undoes all of that increase.
-                */
+       gnm_complex_from_polar (&f12, f1, eta1a);
+       if (eta1b) {
+               gnm_complex_from_polar (&f3, 1, eta1b);
+               gnm_complex_mul (&f12, &f12, &f3);
        }
+       gnm_complex_init (&f3, gnm_cospi (eta1pi), gnm_sinpi (eta1pi));
+       gnm_complex_mul (&f12, &f12, &f3);
+       debye_u_sum (&sum, x, nu, N, TRUE, TRUE);
+       gnm_complex_mul (res, &f12, &sum);
 
-       /* For small x, the factorials will dominate quickly.  */
-       if (gnm_abs (x) < 4)
-               return TRUE;
+       if (0)
+               g_printerr ("D29(%g,%g) = %.20g + %.20g*i\n", x, nu, res->re, res->im);
+}
 
-       /*
-        * The factorials also dominate immediately if x*x/4<|v|.
-        * We must not go too far beyond that.
-        */
-       if (x * x / 16 > gnm_abs (v))
-               return FALSE;
+static gnm_float
+debye_32 (gnm_float x, gnm_float nu, gnm_float eta2, size_t N)
+{
+       gnm_float sqdiff = nu * nu - x * x;
+       gnm_float f = gnm_exp (-eta2) /
+               (gnm_sqrt (2 * M_PIgnum) * gnm_pow (sqdiff, 0.25));
+       gnm_float res;
+       gnm_complex sum;
 
-       return TRUE;
-}
+       debye_u_sum (&sum, x, nu, N, FALSE, FALSE);
+       res = f * sum.re;
 
+       if (0)
+               g_printerr ("D32(%g,%g) = %.20g\n", x, nu, res);
+
+       return res;
+}
 
 static gnm_float
-bessel_j_series (gnm_float x, gnm_float v)
+debye_33 (gnm_float x, gnm_float nu, gnm_float eta2, size_t N)
 {
-       GnmQuad qv, qxh, qfv, qs, qt;
-       int efv;
-       void *state = gnm_quad_start ();
-       gnm_float e, s;
+       gnm_float sqdiff = nu * nu - x * x;
+       gnm_float f = - gnm_sqrt (2 / M_PIgnum) * gnm_exp (eta2) /
+               gnm_pow (sqdiff, 0.25);
+       gnm_float res;
+       gnm_complex sum;
 
-       gnm_quad_init (&qxh, x / 2);
-       gnm_quad_init (&qv, v);
+       debye_u_sum (&sum, x, nu, N, TRUE, FALSE);
+       res = f * sum.re;
 
-       gnm_quad_pow (&qt, &e, &qxh, &qv);
+       if (0)
+               g_printerr ("D33(%g,%g) = %.20g\n", x, nu, res);
 
-       switch (qfactf (v, &qfv, &efv)) {
-       case 0:
-               gnm_quad_div (&qt, &qt, &qfv);
-               e -= efv;
-               break;
-       case 1:
-               qt = gnm_quad_zero;
-               e = 0;
-               break;
-       default:
-               gnm_quad_init (&qt, gnm_nan);
-               e = 0;
-               break;
+       return res;
+}
+
+static gnm_float
+integral_83_coshum1 (gnm_float d, gnm_float sinv, gnm_float cosv,
+                    gnm_float sinbeta, gnm_float cosbeta)
+{
+       gnm_float r = 0, todd, teven, dd, cotv;
+       int i;
+
+       if (gnm_abs (d) > 0.1)
+               return (d * cosbeta - (sinv - sinbeta)) / sinv;
+
+       cotv = cosv / sinv;
+       dd = d * d;
+       teven = 1;
+       todd = d;
+       for (i = 2; i < 100; i++) {
+               gnm_float t;
+               if (i & 1) {
+                       todd *= -dd / (i == 3 ? 3 : i * (i - 3));
+                       t = todd * cotv;
+               } else {
+                       teven *= -dd / (i * (i - 3));
+                       t = teven;
+               }
+               r += t;
+               if (gnm_abs (t) <= gnm_abs (r) * (GNM_EPSILON / 16))
+                       break;
        }
 
-       qs = qt;
-       s = gnm_quad_value (&qs);
-       if (gnm_finite (s) && s != 0) {
-               int k;
-               GnmQuad qxh2;
+       if (0) {
+               gnm_float ref = (d * cosbeta - (sinv - sinbeta)) / sinv;
+               g_printerr ("coshum1(d=%g): %g %g\n", d, ref, r);
+       }
 
-               gnm_quad_mul (&qxh2, &qxh, &qxh);
+       return r;
+}
 
-               for (k = 1; k < 200; k++) {
-                       GnmQuad qa, qb;
-                       gnm_float t;
+static gnm_float
+integral_83_cosdiff (gnm_float d, gnm_float v,
+                    gnm_float sinbeta, gnm_float cosbeta)
+{
+       gnm_float s = 0;
+       gnm_float t = 1;
+       size_t i;
+       gboolean debug = FALSE;
+
+       g_return_val_if_fail (gnm_abs (d) < 1, gnm_nan);
+
+       for (i = 1; i < 100; i += 2) {
+               t *= -d / i;
+               s += sinbeta * t;
+               t *= d / (i + 1);
+               s += cosbeta * t;
+               if (gnm_abs (t) <= gnm_abs (s) * (GNM_EPSILON / 16))
+                       break;
+       }
 
-                       gnm_quad_mul (&qt, &qt, &qxh2);
-                       gnm_quad_init (&qa, -k);
-                       gnm_quad_sub (&qb, &qv, &qa);
-                       gnm_quad_mul (&qa, &qa, &qb);
-                       gnm_quad_div (&qt, &qt, &qa);
-                       t = gnm_quad_value (&qt);
-                       if (t == 0)
-                               break;
-                       gnm_quad_add (&qs, &qs, &qt);
-                       s = gnm_quad_value (&qs);
-                       if (k > 5 &&
-                           gnm_abs (t) <= GNM_EPSILON / 1024 * gnm_abs (s) &&
-                           gnm_abs (k + v) > 2)
-                               break;
+       if (debug) {
+               gnm_float ref = gnm_cos (v) - cosbeta;
+               g_printerr ("cosdiff(d=%g): %g %g\n", d, ref, s);
+       }
+
+       return s;
+}
+
+static void
+integral_83_integrand (gnm_complex *res, gnm_float v, gnm_float const *args)
+{
+       gnm_float x = args[0];
+       gnm_float nu = args[1];
+       gnm_float beta = args[2];
+       gnm_float du_dv, phi1, xphi1;
+       gnm_float sinv = gnm_sin (v);
+       gboolean debug = FALSE;
+
+       if (sinv <= 0) {
+               // Either end
+               du_dv = gnm_nan;
+               phi1 = gnm_ninf;
+       } else {
+               gnm_float vmbeta = v - beta;
+               gnm_float cosv = gnm_cos (v);
+               gnm_float cosbeta = nu / x;
+               gnm_float sinbeta = gnm_sqrt (1 - cosbeta * cosbeta);
+               gnm_float coshum1 = integral_83_coshum1 (vmbeta, sinv, cosv,
+                                                        sinbeta, cosbeta);
+               gnm_float sinhu = gnm_sqrt (coshum1 * (coshum1 + 2));
+               // Deviation: use coshum1 here to avoid cancellation
+               gnm_float u = gnm_log1p (sinhu + coshum1);
+               gnm_float du = (gnm_sin (v - beta) -
+                               (v - beta) * cosbeta * cosv);
+               // Erratum: fix sign of u.  See Watson 8.32
+               if (v < beta)
+                       u = -u, sinhu = -sinhu;
+               if (gnm_abs (vmbeta) < 0.1) {
+                       phi1 = integral_83_cosdiff (vmbeta, v, sinbeta, cosbeta) * sinhu +
+                               gnm_sinhumu (u) * cosbeta;
+               } else {
+                       phi1 = cosv * sinhu - cosbeta * u;
+               }
+               du_dv =  du ? du / (sinhu * sinv * sinv) : 0;
+               if (debug) {
+                       g_printerr ("beta = %g\n", beta);
+                       g_printerr ("v-beta = %g\n", v-beta);
+                       g_printerr ("phi1 = %g\n", phi1);
+                       g_printerr ("du_dv = %g\n", du_dv);
+                       g_printerr ("coshum1 = %g\n", coshum1);
+                       g_printerr ("sinhu = %g\n", sinhu);
                }
        }
 
-       s = gnm_ldexp (s, (int)CLAMP (e, G_MININT, G_MAXINT));
+       xphi1 = x * phi1;
+       if (xphi1 == gnm_ninf) {
+               // "exp" wins.
+               gnm_complex_init (res, 0, 0);
+       } else {
+               gnm_float exphi1 = gnm_exp (xphi1);
+               gnm_complex_init (res, du_dv * exphi1, exphi1);
+       }
 
-       gnm_quad_end (state);
+       if (debug)
+               g_printerr ("i83(%g) = %.20g + %.20g*i\n", v, res->re, res->im);
+}
 
-       return s;
+static void
+integral_83_alt_integrand (gnm_complex *res, gnm_float t, gnm_float const *args)
+{
+       // v = t^vpow; dv/dt = vpow*t^(vpow-1)
+       gnm_float vpow = args[3];
+       integral_83_integrand (res, gnm_pow (t, vpow), args);
+       gnm_complex_scale_real (res, vpow * gnm_pow (t, vpow - 1));
+}
+
+static void
+integral_83 (gnm_complex *res, gnm_float x, gnm_float nu, size_t N,
+            gnm_float vpow)
+{
+       // -i/Pi * exp(i*(x*sin(beta)-nu*beta)) *
+       //    Integrate[(du/dv+i)*exp(x*phi1),{v,0,Pi}]
+       //
+       // beta = acos(nu/x)
+       // u = acosh((sin(beta)+(v-beta)*cos(beta))/sin(v))
+       // du/dv = (sin(v-beta)-(v-beta)*cos(beta)*cos(v)) /
+       //            (sinh(u)*sin^2(v))
+       // phi1 = cos(v)*sinh(u) - cos(beta)*u
+       //
+       // When vpow is not 1 we change variable as v=t^vpow numerically.
+       // (I.e., the trapezoid points will be evenly spaced in t-space
+       // instead of v-space.)
+
+       // x >= 9 && nu < x - 1.5*crbt(x)
+
+       gnm_complex I, f1, f2;
+       gnm_float beta = gnm_acos (nu / x);
+       gnm_float xsinbeta = gnm_sqrt (x * x - nu * nu);
+       gnm_float refx = beta;
+       gnm_float L = 0;
+       gnm_float H = M_PIgnum;
+       gnm_float args[4] = { x, nu, beta, vpow };
+       ComplexIntegrand integrand;
+
+       complex_shink_integral_range (&L, &H, refx,
+                                     integral_83_integrand, args);
+
+       if (vpow != 1) {
+               L = gnm_pow (L, 1 / vpow);
+               H = gnm_pow (H, 1 / vpow);
+               integrand = integral_83_alt_integrand;
+       } else {
+               // We could use the indirect path above, but let's go direct
+               integrand = integral_83_integrand;
+       }
+
+       complex_trapezoid_integral (&I, N, L, H, integrand, args);
+
+       gnm_complex_from_polar (&f1, 1, xsinbeta - nu * beta);
+       gnm_complex_init (&f2, 0, -1 / M_PIgnum);
+       gnm_complex_mul (&I, &I, &f1);
+       gnm_complex_mul (res, &I, &f2);
+
+       if (0)
+               g_printerr ("I83(%g,%g) = %.20g + %.20g*i\n",
+                           x, nu, res->re, res->im);
+
+}
+
+static void
+integral_105_126_integrand (gnm_complex *res, gnm_float u, gnm_float const *args)
+{
+       gnm_float x = args[0];
+       gnm_float nu = args[1];
+       gnm_complex_init (res, gnm_exp (x * gnm_sinh (u) - nu * u), 0);
+}
+
+static void
+integral_105_126 (gnm_complex *res, gnm_float x, gnm_float nu, gboolean qH0)
+{
+       // -i/Pi * Integrate[Exp[x*Sinh[u]-nu*u],{u,-Infinity,H}]
+       // where H is either 0 or alpha, see below.
+
+       // Deviation: the analysis doesn't seem to consider the case where
+       // nu < x which occurs for (126).  In that case the integrand takes
+       // its maximum at 0.
+
+       gnm_float args[2] = { x, nu };
+       gnm_complex I;
+       gnm_float refx = (nu < x) ? 0 : -gnm_acosh (nu / x);
+       // For the nu~x case, we have sinh(u)-u = u^3/6 + ...
+       gnm_float L = refx - MAX (gnm_cbrt (6 * 50 / ((nu + x) / 2)), 50.0 / MIN (nu, x));
+       gnm_float H = qH0 ? 0 : -refx;
+
+       complex_shink_integral_range (&L, &H, refx,
+                                     integral_105_126_integrand, args);
+
+       complex_legendre_integral (&I, 45, L, H,
+                                  integral_105_126_integrand, args);
+
+       gnm_complex_init (res, 0, I.re / -M_PIgnum);
+
+       if (0)
+               g_printerr ("I105(%g,%g) = %.20g * i\n", x, nu, res->im);
+}
+
+static void
+integral_106_integrand (gnm_complex *res, gnm_float v, gnm_float const *args)
+{
+       gnm_float x = args[0];
+       gnm_float nu = args[1];
+
+       gnm_float sinv = gnm_sin (v);
+       gnm_float coshalpha = nu / x;
+       gnm_float coshu = coshalpha * (v ? (v / sinv) : 1);
+       // FIXME: u and sinhu are dubious, numerically
+       gnm_float u = gnm_acosh (coshu);
+       gnm_float sinhu = gnm_sinh (u);
+       gnm_float xphi3 = x * sinhu * gnm_cos (v) - nu * u;
+       gnm_float exphi3 = gnm_exp (xphi3);
+
+       gnm_float num = nu * gnm_sinv_m_v_cosv (v, sinv);
+       gnm_float den = x * sinv * sinv * sinhu;
+       gnm_float du_dv = v ? num / den : 0;
+
+       gnm_complex_init (res, exphi3 * du_dv, exphi3);
+
+       if (0) {
+               g_printerr ("u=%g\n", u);
+               g_printerr ("coshalpha=%g\n", coshalpha);
+               g_printerr ("cosh(u)=%g\n", coshu);
+               g_printerr ("sinh(u)=%g\n", sinhu);
+               g_printerr ("xphi3=%g\n", xphi3);
+               g_printerr ("i106(%g) = %.20g + %.20g*i\n", v, res->re, res->im);
+       }
+}
+
+static void
+integral_106 (gnm_complex *res, gnm_float x, gnm_float nu)
+{
+       // -i/Pi * Integrate[Exp[x*phi3[v]]*(i+du/dv),{v,0,Pi}]
+       //
+       // alpha = acosh(nu/x)
+       // u(v) = acosh(nu/x * v/sin(v))
+       // du/dv = cosh(alpha)*(sin(v)-v*cos(v))/(sin^2(v)*sinh(u(v)))
+       // phi3(v) = sinh(u)*cos(v) - cosh(alpha)*u(v)
+
+       // Note: 2 < x < nu.
+
+       gnm_complex I, mipi;
+       gnm_float L = 0, H = M_PIgnum;
+       gnm_float args[2] = { x, nu };
+
+       complex_shink_integral_range (&L, &H, 0, integral_106_integrand, args);
+
+       complex_legendre_integral (&I, 45, L, H,
+                                  integral_106_integrand, args);
+
+       gnm_complex_init (&mipi, 0, -1 / M_PIgnum);
+       gnm_complex_mul (res, &I, &mipi);
+
+       if (0)
+               g_printerr ("I106(%g,%g) = %.20g + %.20g*i\n", x, nu, res->re, res->im);
+}
+
+static gnm_float
+integral_127_u (gnm_float v)
+{
+       static const gnm_float c[] = {
+               GNM_const(0.57735026918962576451),
+               GNM_const(0.025660011963983367312),
+               GNM_const(0.0014662863979419067035),
+               GNM_const(0.000097752426529460446901),
+               GNM_const(7.4525058224720927532e-6),
+               GNM_const(6.1544207267743329429e-7),
+               GNM_const(5.2905118464628039046e-8),
+               GNM_const(4.6529126736818620163e-9),
+               GNM_const(4.1606321535886269061e-10),
+               GNM_const(3.7712142304302013266e-11),
+               GNM_const(3.4567362099184451359e-12),
+               GNM_const(3.1977726302920313260e-13),
+               GNM_const(2.9808441172607163378e-14),
+               GNM_const(2.7965280211260193677e-15)
+       };
+       unsigned ci;
+       gnm_float vv, u = 0;
+
+       if (v >= 1)
+               return acosh (v / gnm_sin (v));
+
+       // Above formula will suffer from cancellation
+       vv = v * v;
+       for (ci = G_N_ELEMENTS(c); ci-- > 0; )
+               u = u * vv + c[ci];
+       u *= v;
+
+       if (0) {
+               gnm_float ref = acosh (v / gnm_sin (v));
+               g_printerr ("XXX: %g %g\n", ref, u);
+       }
+
+       return u;
+}
+
+static gnm_float
+integral_127_u_m_sinhu_cos_v (gnm_float v, gnm_float u, gnm_float sinhu)
+{
+       static const gnm_float c[] = {
+               /*  3 */ GNM_const(0.25660011963983367312),
+               /*  5 */ GNM_const(0.0),
+               /*  7 */ GNM_const(0.00097752426529460446901),
+               /*  9 */ GNM_const(0.000072409204836637368075),
+               /* 11 */ GNM_const(7.4478039260541292877e-6),
+               /* 13 */ GNM_const(7.4130822294291683120e-7),
+               /* 15 */ GNM_const(7.4423844019777464899e-8),
+               /* 17 */ GNM_const(7.4866591579915856176e-9),
+               /* 19 */ GNM_const(7.5416412192891756316e-10),
+               /* 21 */ GNM_const(7.6048685642328096017e-11),
+               /* 23 */ GNM_const(7.6748139912232122716e-12),
+               /* 25 */ GNM_const(7.7502621827532506438e-13),
+               /* 27 */ GNM_const(7.8302824791617646275e-14),
+               /* 29 */ GNM_const(7.9141968028287716142e-15),
+               /* 31 */ GNM_const(8.0015150114119176413e-16),
+               /* 33 */ GNM_const(8.0918754232915038797e-17),
+               /* 35 */ GNM_const(8.1850043476015809121e-18)
+       };
+       unsigned ci;
+       gnm_float vv, r = 0;
+
+       if (v >= 1)
+               return u - sinhu * gnm_cos (v);
+
+       // Above formula will suffer from cancellation
+       vv = v * v;
+       for (ci = G_N_ELEMENTS(c); ci-- > 0; )
+               r = r * vv + c[ci];
+       r *= v * vv;
+
+       if (0) {
+               gnm_float ref = u - sinhu * gnm_cos (v);
+               g_printerr ("XXX: %g %g %g\n", ref, r, ref - r);
+       }
+
+       return r;
+}
+
+static void
+integral_127_integrand (gnm_complex *res, gnm_float v, gnm_float const *args)
+{
+       gnm_float x = args[0];
+       gnm_float nu = args[1];
+
+       gnm_float u = integral_127_u (v);
+       // Deviation: Matviyenko uses taylor expansion for sinh for u < 1.
+       // There is no need, assuming a reasonable sinh implementation.
+       gnm_float sinhu = gnm_sinh (u);
+       gnm_float diff = integral_127_u_m_sinhu_cos_v (v, u, sinhu);
+       gnm_float sinv = gnm_sin (v);
+       gnm_float num = gnm_sinv_m_v_cosv (v, sinv);
+       gnm_float den = sinv * sinv * sinhu;
+       gnm_float du_dv = v ? num / den : 0;
+
+       gnm_complex xphi4, exphi4, i_du_dv;
+
+       gnm_complex_init (&xphi4, x * -diff + (x - nu) * u, (x - nu) * v);
+       gnm_complex_exp (&exphi4, &xphi4);
+       gnm_complex_init (&i_du_dv, du_dv, 1);
+       gnm_complex_mul (res, &exphi4, &i_du_dv);
+}
+
+static void
+integral_127 (gnm_complex *res, gnm_float x, gnm_float nu)
+{
+       // -i/Pi * Integrate[Exp[x*phi4[v]]*(i+du/dv),{v,0,Pi}]
+       //
+       // tau = 1-nu/x
+       // u(v) = acosh(v/sin(v))
+       // du/dv = (sin(v)-v*cos(v))/(sin^2(v)*sinh(u(v)))
+       // phi4(v) = sinh(u)*cos(v) - u(v) + tau(u(v) + i * v)
+
+       gnm_complex I, mipi;
+       gnm_float L = 0, H = M_PIgnum;
+       gnm_float args[2] = { x, nu };
+
+       complex_shink_integral_range (&L, &H, 0,
+                                     integral_127_integrand, args);
+
+       complex_legendre_integral (&I, 33, L, H,
+                                  integral_127_integrand, args);
+
+       gnm_complex_init (&mipi, 0, -1 / M_PIgnum);
+       gnm_complex_mul (res, &I, &mipi);
+
+       if (0)
+               g_printerr ("I127(%g,%g) = %.20g + %.20g*i\n", x, nu, res->re, res->im);
+}
+
+static gnm_float
+y_via_j_series (gnm_float nu, const gnm_float *args)
+{
+       gnm_float x = args[0];
+       gnm_float Jnu = bessel_j_series (x, nu);
+       gnm_float Jmnu = bessel_j_series (x, -nu);
+       gnm_float c = gnm_cospi (nu);
+       gnm_float s = gnm_sinpi (nu);
+       gnm_float Ynu = (Jnu * c - Jmnu) / s;
+       if (0) g_printerr ("via: %.20g %.20g %.20g %.20g %.20g\n", x, nu, Jnu, Jmnu, Ynu);
+       return Ynu;
+}
+
+static void
+hankel1_B1 (gnm_complex *res, gnm_float x, gnm_float nu, size_t N)
+{
+       debye_29 (res, x, nu, N);
+}
+
+static void
+hankel1_B2 (gnm_complex *res, gnm_float x, gnm_float nu, size_t N)
+{
+       gnm_float q = nu / x;
+       gnm_float d = gnm_sqrt (q * q - 1);
+       gnm_float eta2 = nu * gnm_log (q + d) - gnm_sqrt (nu * nu - x * x);
+
+       res->re = debye_32 (x, nu, eta2, N);
+       res->im = debye_33 (x, nu, eta2, N);
+}
+
+static void
+hankel1_A1 (gnm_complex *res, gnm_float x, gnm_float nu)
+{
+       gnm_float rnu = gnm_floor (nu + 0.5);
+       gnm_float Jnu = bessel_j_series (x, nu);
+       gnm_float Ynu;
+
+       if (gnm_abs (nu - rnu) > 5e-4) {
+               gnm_float Jmnu = bessel_j_series (x, -nu);
+               gnm_float c = gnm_cospi (nu);
+               gnm_float s = gnm_sinpi (nu);
+               Ynu = (Jnu * c - Jmnu) / s;
+       } else {
+               size_t N = 6;
+               gnm_float dnu = 1e-3;
+               gnm_float args[1] = { x };
+               Ynu = chebyshev_interpolant (N, rnu - dnu, rnu + dnu, nu,
+                                            y_via_j_series, args);
+       }
+       gnm_complex_init (res, Jnu, Ynu);
+}
+
+static void
+hankel1_A2 (gnm_complex *res, gnm_float x, gnm_float nu)
+{
+       gnm_complex I1, I2;
+       integral_105_126 (&I1, x, nu, FALSE);
+       integral_106 (&I2, x, nu);
+       gnm_complex_add (res, &I1, &I2);
+}
+
+static void
+hankel1_A3 (gnm_complex *res, gnm_float x, gnm_float nu, gnm_float g)
+{
+       // Deviation: Matviyenko says to change variable to v = t^4 for g < 5.
+       // That works wonders for BesselJ[9,12.5], but is too sudden for
+       // BesselJ[1,10].  Instead, gradually move from power of 1 to power
+       // of 4.  The was the power is lowered is ad hoc.
+       //
+       // Also, we up the number of points from 37 to 47.
+
+       if (g > 5)
+               integral_83 (res, x, nu, 25, 1);
+       else if (g > 4)
+               integral_83 (res, x, nu, 47, 2);
+       else if (g > 3)
+               integral_83 (res, x, nu, 47, 3);
+       else
+               integral_83 (res, x, nu, 47, 4);
+}
+
+static void
+hankel1_A4 (gnm_complex *res, gnm_float x, gnm_float nu)
+{
+       gnm_complex J1, J2;
+       // Deviation: when Matviyenko says that (126) is the same as (105)
+       // with alpha=0, he is glossing over the finer points.  When he should
+       // have said is that the alpha in the limit is replaced by 0 and
+       // that the cosh(alpha) inside is replaced textually by nu/x.  (We may
+       // have nu<x and alpha isn't even defined in that case.)
+       integral_105_126 (&J1, x, nu, TRUE);
+       integral_127 (&J2, x, nu);
+       gnm_complex_add (res, &J1, &J2);
+}
+
+/* ------------------------------------------------------------------------ */
+
+// This follows "On the Evaluation of Bessel Functions" by Gregory Matviyenko.
+// Research report YALEU/DCS/RR-903, Yale, Dept. of Comp. Sci., 1992.
+//
+// Note: there are a few deviations are fixes in this code.  These are marked
+// with "deviation" or "erratum".
+
+static void
+hankel1 (gnm_complex *res, gnm_float x, gnm_float nu)
+{
+       gnm_float cbrtx, g;
+
+       gnm_complex_init (res, gnm_nan, gnm_nan);
+       if (gnm_isnan (x) || gnm_isnan (nu))
+               return;
+
+       g_return_if_fail (x >= 0);
+
+       // Deviation: make this work for negative nu also.
+       if (nu < 0) {
+               gnm_complex Hmnu, r;
+
+               hankel1 (&Hmnu, x, -nu);
+               if (0) g_printerr ("H_{%g,%g} = %.20g + %.20g * i\n", -nu, x, Hmnu.re, Hmnu.im);
+               gnm_complex_init (&r, gnm_cospi (-nu), gnm_sinpi (-nu));
+               gnm_complex_mul (res, &Hmnu, &r);
+               return;
+       }
+
+       cbrtx = gnm_cbrt (x);
+       g = gnm_abs (x - nu) / cbrtx;
+
+       if (x >= 17 && g >= 6.5) {
+               // Algorithm B
+               size_t N;
+               if (g < 7)
+                       N = 17;
+               else if (g < 10)
+                       N = 13;
+               else if (g < 23)
+                       N = 9;
+               else
+                       N = 5;
+
+               if (nu < x)
+                       hankel1_B1 (res, x, nu, N);
+               else
+                       hankel1_B2 (res, x, nu, N);
+       } else {
+               // Algorithm A
+               // Deviation: we use the series on a wider domain as our
+               // series code uses quad precision.
+               if (bessel_j_series_domain (x, nu))
+                       hankel1_A1 (res, x, nu);
+               else if (nu > x && g > 1.5)
+                       hankel1_A2 (res, x, nu);
+               else if (x >= 9 && nu < x && g > 1.5)
+                       hankel1_A3 (res, x, nu, g);
+               else
+                       hankel1_A4 (res, x, nu);
+       }
 }
 
 /* ------------------------------------------------------------------------ */
@@ -2368,15 +2433,21 @@ gnm_bessel_i (gnm_float x, gnm_float alpha)
 gnm_float
 gnm_bessel_j (gnm_float x, gnm_float alpha)
 {
+       if (gnm_isnan (x) || gnm_isnan (alpha))
+               return x + alpha;
+
        if (x < 0) {
                if (alpha != gnm_floor (alpha))
                        return gnm_nan;
 
                return gnm_fmod (alpha, 2) == 0
-                       ? bessel_j (-x, alpha)  /* Even for even alpha */
-                       : 0 - bessel_j (-x, alpha);  /* Odd for odd alpha */
-       } else
-               return bessel_j (x, alpha);
+                       ? gnm_bessel_j (-x, alpha)  /* Even for even alpha */
+                       : 0 - gnm_bessel_j (-x, alpha);  /* Odd for odd alpha */
+       } else {
+               gnm_complex H1;
+               hankel1 (&H1, x, alpha);
+               return H1.re;
+       }
 }
 
 gnm_float
@@ -2388,7 +2459,21 @@ gnm_bessel_k (gnm_float x, gnm_float alpha)
 gnm_float
 gnm_bessel_y (gnm_float x, gnm_float alpha)
 {
-       return bessel_y (x, alpha);
+       if (gnm_isnan (x) || gnm_isnan (alpha))
+               return x + alpha;
+
+       if (x < 0) {
+               if (alpha != gnm_floor (alpha))
+                       return gnm_nan;
+
+               return gnm_fmod (alpha, 2) == 0
+                       ? gnm_bessel_y (-x, alpha)  /* Even for even alpha */
+                       : 0 - gnm_bessel_y (-x, alpha);  /* Odd for odd alpha */
+       } else {
+               gnm_complex H1;
+               hankel1 (&H1, x, alpha);
+               return H1.im;
+       }
 }
 
 /* ------------------------------------------------------------------------- */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]