Blame specfunc/legendre_source.c

Packit 67cb25
/* specfunc/legendre_source.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2009-2013 Patrick Alken
Packit 67cb25
 * 
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 * 
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 * 
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software
Packit 67cb25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
/* define various macros for functions below */
Packit 67cb25
Packit 67cb25
#define CONCAT2x(a,b)   a ## _ ## b 
Packit 67cb25
#define CONCAT3x(a,b,c) a ## _ ## b ## _ ## c
Packit 67cb25
Packit 67cb25
#if defined(LEGENDRE)
Packit 67cb25
#define FUNCTION(dir,name) CONCAT2x(dir,name)
Packit 67cb25
#define OUTPUT result_array
Packit 67cb25
#define OUTPUT_ARG double result_array[]
Packit 67cb25
Packit 67cb25
#elif defined(LEGENDRE_DERIV)
Packit 67cb25
#define FUNCTION(dir,name) CONCAT3x(dir,deriv,name)
Packit 67cb25
#define OUTPUT result_array, result_deriv_array
Packit 67cb25
#define OUTPUT_ARG double result_array[], double result_deriv_array[]
Packit 67cb25
Packit 67cb25
#elif defined(LEGENDRE_DERIV_ALT)
Packit 67cb25
#define FUNCTION(dir,name) CONCAT3x(dir,deriv_alt,name)
Packit 67cb25
#define OUTPUT result_array, result_deriv_array
Packit 67cb25
#define OUTPUT_ARG double result_array[], double result_deriv_array[]
Packit 67cb25
#define LEGENDRE_DERIV
Packit 67cb25
Packit 67cb25
#elif defined(LEGENDRE_DERIV2)
Packit 67cb25
#define FUNCTION(dir,name) CONCAT3x(dir,deriv2,name)
Packit 67cb25
#define OUTPUT result_array, result_deriv_array, result_deriv2_array
Packit 67cb25
#define OUTPUT_ARG double result_array[], double result_deriv_array[], double result_deriv2_array[]
Packit 67cb25
#define LEGENDRE_DERIV
Packit 67cb25
Packit 67cb25
#elif defined(LEGENDRE_DERIV2_ALT)
Packit 67cb25
#define FUNCTION(dir,name) CONCAT3x(dir,deriv2_alt,name)
Packit 67cb25
#define OUTPUT result_array, result_deriv_array, result_deriv2_array
Packit 67cb25
#define OUTPUT_ARG double result_array[], double result_deriv_array[], double result_deriv2_array[]
Packit 67cb25
#define LEGENDRE_DERIV
Packit 67cb25
#define LEGENDRE_DERIV2
Packit 67cb25
#define LEGENDRE_DERIV_ALT
Packit 67cb25
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
static int FUNCTION (legendre, array_schmidt_e)
Packit 67cb25
(const size_t lmax, const double x, const double csphase, OUTPUT_ARG);
Packit 67cb25
static int FUNCTION(legendre, array_none_e)
Packit 67cb25
(const size_t lmax, const double x, const double csphase, OUTPUT_ARG);
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_sf_legendre_array()
Packit 67cb25
Packit 67cb25
Inputs: norm                - normlization type
Packit 67cb25
        lmax                - maximum degree
Packit 67cb25
        x                   - input argument
Packit 67cb25
        result_array        - (output) normalized P_{lm}
Packit 67cb25
        result_deriv_array  - (output) normalized P'_{lm}
Packit 67cb25
        result_deriv2_array - (output) normalized P''_{lm}
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
FUNCTION (gsl_sf_legendre, array)
Packit 67cb25
(const gsl_sf_legendre_t norm, const size_t lmax, const double x,
Packit 67cb25
 OUTPUT_ARG)
Packit 67cb25
{
Packit 67cb25
  int s = FUNCTION (gsl_sf_legendre, array_e)(norm, lmax, x, 1.0, OUTPUT);
Packit 67cb25
  return s;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_sf_legendre_array_e()
Packit 67cb25
Packit 67cb25
Inputs: norm                - normlization type
Packit 67cb25
        lmax                - maximum degree
Packit 67cb25
        x                   - input argument
Packit 67cb25
        csphase             - Condon-Shortley phase
Packit 67cb25
        result_array        - (output) normalized P_{lm}
Packit 67cb25
        result_deriv_array  - (output) normalized P'_{lm}
Packit 67cb25
        result_deriv2_array - (output) normalized P''_{lm}
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
FUNCTION (gsl_sf_legendre, array_e)
Packit 67cb25
(const gsl_sf_legendre_t norm, const size_t lmax, const double x,
Packit 67cb25
 const double csphase, OUTPUT_ARG)
Packit 67cb25
{
Packit 67cb25
  int s;
Packit 67cb25
  const size_t nlm = gsl_sf_legendre_nlm(lmax);
Packit 67cb25
#if !defined(LEGENDRE_DERIV_ALT)
Packit 67cb25
  size_t i;
Packit 67cb25
#if defined(LEGENDRE_DERIV) || defined(LEGENDRE_DERIV2)
Packit 67cb25
  const double u = sqrt((1.0 - x) * (1.0 + x));
Packit 67cb25
  const double uinv = 1.0 / u;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
  const double uinv2 = uinv * uinv;
Packit 67cb25
#endif
Packit 67cb25
#endif
Packit 67cb25
  double fac1 = 0.0, fac2 = 0.0; /* normalization factors */
Packit 67cb25
Packit 67cb25
  if (norm == GSL_SF_LEGENDRE_NONE)
Packit 67cb25
    {
Packit 67cb25
      /* compute P_{lm}(x) */
Packit 67cb25
      s = FUNCTION(legendre,array_none_e)(lmax, x, csphase, OUTPUT);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      /* compute S_{lm}(x) */
Packit 67cb25
      s = FUNCTION(legendre,array_schmidt_e)(lmax, x, csphase, OUTPUT);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
#if !defined(LEGENDRE_DERIV_ALT)
Packit 67cb25
  /* scale derivative arrays to recover P'(x) and P''(x) */
Packit 67cb25
  for (i = 0; i < nlm; ++i)
Packit 67cb25
    {
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      double dp = result_deriv_array[i];
Packit 67cb25
      double d2p = result_deriv2_array[i];
Packit 67cb25
Packit 67cb25
      result_deriv2_array[i] = (d2p - x * uinv * dp) * uinv2;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      result_deriv_array[i] *= -uinv;
Packit 67cb25
#endif
Packit 67cb25
    }
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
  /* apply scaling for requested normalization */
Packit 67cb25
  if (norm == GSL_SF_LEGENDRE_SCHMIDT || norm == GSL_SF_LEGENDRE_NONE)
Packit 67cb25
    {
Packit 67cb25
      return s;
Packit 67cb25
    }
Packit 67cb25
  else if (norm == GSL_SF_LEGENDRE_SPHARM)
Packit 67cb25
    {
Packit 67cb25
      fac1 = 1.0 / sqrt(4.0 * M_PI);
Packit 67cb25
      fac2 = 1.0 / sqrt(8.0 * M_PI);
Packit 67cb25
    }
Packit 67cb25
  else if (norm == GSL_SF_LEGENDRE_FULL)
Packit 67cb25
    {
Packit 67cb25
      fac1 = 1.0 / sqrt(2.0);
Packit 67cb25
      fac2 = 1.0 / sqrt(4.0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * common code for different normalizations
Packit 67cb25
   * P_{l0} = fac1 * sqrt(2l + 1) * S_{l0}
Packit 67cb25
   * P_{lm} = fac2 * sqrt(2l + 1) * S_{lm}, m > 0
Packit 67cb25
   */
Packit 67cb25
  {
Packit 67cb25
    size_t l, m;
Packit 67cb25
    size_t twoellp1 = 1; /* 2l + 1 */
Packit 67cb25
    double *sqrts = &(result_array[nlm]);
Packit 67cb25
Packit 67cb25
    for (l = 0; l <= lmax; ++l)
Packit 67cb25
      {
Packit 67cb25
        result_array[gsl_sf_legendre_array_index(l, 0)] *=
Packit 67cb25
          sqrts[twoellp1] * fac1;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
        result_deriv_array[gsl_sf_legendre_array_index(l, 0)] *=
Packit 67cb25
          sqrts[twoellp1] * fac1;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
        result_deriv2_array[gsl_sf_legendre_array_index(l, 0)] *=
Packit 67cb25
          sqrts[twoellp1] * fac1;
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
        for (m = 1; m <= l; ++m)
Packit 67cb25
          {
Packit 67cb25
            result_array[gsl_sf_legendre_array_index(l, m)] *=
Packit 67cb25
              sqrts[twoellp1] * fac2;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
            result_deriv_array[gsl_sf_legendre_array_index(l, m)] *=
Packit 67cb25
              sqrts[twoellp1] * fac2;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
            result_deriv2_array[gsl_sf_legendre_array_index(l, m)] *=
Packit 67cb25
              sqrts[twoellp1] * fac2;
Packit 67cb25
#endif
Packit 67cb25
          }
Packit 67cb25
Packit 67cb25
        twoellp1 += 2;
Packit 67cb25
      }
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  return s;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
legendre,array_schmidt_e()
Packit 67cb25
  This routine computes Schmidt semi-normalized associated
Packit 67cb25
Legendre polynomials and their first and second derivatives.
Packit 67cb25
Packit 67cb25
Inputs: lmax                - maximum order
Packit 67cb25
        x                   - legendre argument in [-1,1]
Packit 67cb25
        csphase             - -1.0 to include CS phase (-1)^m,
Packit 67cb25
                               1.0 to not include
Packit 67cb25
        result_array        - (output) where to store P_{lm}(x) values
Packit 67cb25
        result_deriv_array  - (output) where to store
Packit 67cb25
                              d/dtheta P_{lm}(x) values
Packit 67cb25
        result_deriv2_array - (output) where to store
Packit 67cb25
                              d^2/dtheta^2 P_{lm}(x) values
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
FUNCTION(legendre, array_schmidt_e)
Packit 67cb25
(const size_t lmax, const double x, const double csphase, OUTPUT_ARG)
Packit 67cb25
{
Packit 67cb25
  if (x > 1.0 || x < -1.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("x is outside [-1,1]", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
#if defined(LEGENDRE_DERIV) || defined(LEGENDRE_DERIV2)
Packit 67cb25
  else if (fabs(x) == 1.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("x cannot equal 1 or -1 for derivative computation", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
#endif
Packit 67cb25
  else if (csphase != 1.0 && csphase != -1.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("csphase has invalid value", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      const double eps = 1.0e-280;
Packit 67cb25
      const double u = sqrt((1.0 - x) * (1.0 + x)); /* sin(theta) */
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      const double uinv = 1.0 / u;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      const double uinv2 = 1.0 / u / u;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV) || defined(LEGENDRE_DERIV2)
Packit 67cb25
      const double xbyu = x * uinv; /* x / u */
Packit 67cb25
#endif
Packit 67cb25
      size_t l, m;
Packit 67cb25
      size_t k, idxmm;
Packit 67cb25
      double plm, /* eps * S(l,m) / u^m */
Packit 67cb25
             pmm; /* eps * S(m,m) / u^m */
Packit 67cb25
      double rescalem;
Packit 67cb25
      double pm1, /* S(l-1,m) */
Packit 67cb25
             pm2; /* S(l-2,m) */
Packit 67cb25
      size_t nlm = gsl_sf_legendre_nlm(lmax);
Packit 67cb25
      double *sqrts = &(result_array[nlm]);
Packit 67cb25
Packit 67cb25
      /* precompute square root factors for recurrence */
Packit 67cb25
      legendre_sqrts(lmax, sqrts);
Packit 67cb25
Packit 67cb25
      /* initial values S(0,0) and S(1,0) */
Packit 67cb25
      pm2 = 1.0; /* S(0,0) */
Packit 67cb25
      pm1 = x;   /* S(1,0) */
Packit 67cb25
Packit 67cb25
      result_array[0] = pm2;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      result_deriv_array[0] = 0.0;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      result_deriv2_array[0] = 0.0;
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
      if (lmax == 0)
Packit 67cb25
        return GSL_SUCCESS;
Packit 67cb25
Packit 67cb25
      result_array[1] = pm1;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      result_deriv_array[1] = -u;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      result_deriv2_array[1] = -x;
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
      /* Compute S(l,0) for l=2..lmax, no scaling required */
Packit 67cb25
Packit 67cb25
      k = 1; /* idx(1,0) */
Packit 67cb25
      for (l = 2; l <= lmax; ++l)
Packit 67cb25
        {
Packit 67cb25
          double linv = 1.0 / (double)l;
Packit 67cb25
Packit 67cb25
          k += l;  /* idx(l,m) = idx(l-1,m) + l */
Packit 67cb25
Packit 67cb25
          plm = (2.0 - linv) * x * pm1 - (1.0 - linv) * pm2;
Packit 67cb25
          result_array[k] = plm;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
          result_deriv_array[k] = uinv * l * (x * plm - pm1);
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
          result_deriv2_array[k] = -(double) l * (l + 1.0) * plm -
Packit 67cb25
                                   xbyu * result_deriv_array[k];
Packit 67cb25
#endif
Packit 67cb25
          pm2 = pm1;
Packit 67cb25
          pm1 = plm;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* Compute S(m,m), S(m+1,m) and S(l,m) */
Packit 67cb25
Packit 67cb25
      /*
Packit 67cb25
       * pi_m = Prod_{i=2}^m sqrt[ (2m - 1) / (2m) ]
Packit 67cb25
       * but pi_1 = 1.0, so initialize to sqrt(2) so that
Packit 67cb25
       * the first m = 1 iteration of the loop will reset it
Packit 67cb25
       * to 1.0. Starting with m = 2 it will begin accumulating
Packit 67cb25
       * the correct terms.
Packit 67cb25
       *
Packit 67cb25
       * pmm = S(m,m) * eps / u^m = pi_m
Packit 67cb25
       */
Packit 67cb25
      pmm = sqrt(2.0) * eps;
Packit 67cb25
Packit 67cb25
      rescalem = 1.0 / eps;
Packit 67cb25
      idxmm = 0; /* tracks idx(m,m), initialize to idx(0,0) */
Packit 67cb25
Packit 67cb25
      for (m = 1; m < lmax; ++m)
Packit 67cb25
        {
Packit 67cb25
          /* rescalem = u^m / eps */
Packit 67cb25
          rescalem *= u;
Packit 67cb25
Packit 67cb25
          /*
Packit 67cb25
           * compute:
Packit 67cb25
           * S(m,m) = u * sqrt((2m - 1) / (2m)) S(m-1,m-1) = u^m * pi_m
Packit 67cb25
           * d_t S(m,m) = m * x / u * S(m,m)
Packit 67cb25
           */
Packit 67cb25
Packit 67cb25
          idxmm += m + 1; /* idx(m,m) = idx(m-1,m-1) + m + 1 */
Packit 67cb25
          pmm *= csphase * sqrts[2 * m - 1] / sqrts[2 * m]; /* S(m,m) * eps / u^m */
Packit 67cb25
          result_array[idxmm] = pmm * rescalem;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
          result_deriv_array[idxmm] = m * xbyu * result_array[idxmm];
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
          result_deriv2_array[idxmm] =
Packit 67cb25
            m * (uinv2 * m - (m + 1.0)) * result_array[idxmm] -
Packit 67cb25
            xbyu * result_deriv_array[idxmm];
Packit 67cb25
#endif
Packit 67cb25
          pm2 = pmm;
Packit 67cb25
Packit 67cb25
          /*
Packit 67cb25
           * compute:
Packit 67cb25
           * S(m+1,m) = sqrt(2 * m + 1) * x * S(m,m)
Packit 67cb25
           * d_t S(m+1,m) = 1/u * ((m+1)*x*S(m+1,m) - sqrt(2*m+1)*S(m,m))
Packit 67cb25
           */
Packit 67cb25
Packit 67cb25
          k = idxmm + m + 1; /* idx(m+1,m) = idx(m,m) + m + 1 */
Packit 67cb25
          pm1 = x * sqrts[2 * m + 1] * pm2;
Packit 67cb25
          result_array[k] = pm1 * rescalem;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
          result_deriv_array[k] =
Packit 67cb25
            uinv * ((m + 1.0) * x * result_array[k] -
Packit 67cb25
                    sqrts[2 * m + 1] * result_array[idxmm]);
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
          result_deriv2_array[k] =
Packit 67cb25
            (m * m * uinv2 - (m + 1.0) * (m + 2.0)) * result_array[k] -
Packit 67cb25
            xbyu * result_deriv_array[k];
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
          /* compute S(l,m) for l=m+2...lmax */
Packit 67cb25
          for (l = m + 2; l <= lmax; ++l)
Packit 67cb25
            {
Packit 67cb25
              k += l; /* idx(l,m) = idx(l-1,m) + l */
Packit 67cb25
              plm =
Packit 67cb25
                (2*l - 1) / sqrts[l + m] / sqrts[l - m] * x * pm1 -
Packit 67cb25
                sqrts[l - m - 1] * sqrts[l + m - 1] /
Packit 67cb25
                sqrts[l + m] / sqrts[l - m] * pm2;
Packit 67cb25
              result_array[k] = plm * rescalem;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
              result_deriv_array[k] =
Packit 67cb25
                uinv * (l * x * result_array[k] -
Packit 67cb25
                        sqrts[l + m] * sqrts[l - m] * result_array[k - l]);
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
              result_deriv2_array[k] =
Packit 67cb25
                (m * m * uinv2 - l * (l + 1.0)) * result_array[k] -
Packit 67cb25
                xbyu * result_deriv_array[k];
Packit 67cb25
#endif
Packit 67cb25
              pm2 = pm1;
Packit 67cb25
              pm1 = plm;
Packit 67cb25
            }
Packit 67cb25
        } /* for (m = 1; m < lmax; ++m) */
Packit 67cb25
Packit 67cb25
      /* compute S(lmax,lmax) */
Packit 67cb25
Packit 67cb25
      rescalem *= u;
Packit 67cb25
      idxmm += m + 1; /* idx(lmax,lmax) */
Packit 67cb25
      pmm *= csphase * sqrts[2 * lmax - 1] / sqrts[2 * lmax];
Packit 67cb25
      result_array[idxmm] = pmm * rescalem;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      result_deriv_array[idxmm] = lmax * xbyu * result_array[idxmm];
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      result_deriv2_array[idxmm] =
Packit 67cb25
        lmax * (uinv2 * lmax - (lmax + 1.0)) * result_array[idxmm] -
Packit 67cb25
        xbyu * result_deriv_array[idxmm];
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
legendre_array_none_e()
Packit 67cb25
  This routine computes unnormalized associated Legendre polynomials
Packit 67cb25
and their derivatives.
Packit 67cb25
Packit 67cb25
Inputs: lmax                - maximum order
Packit 67cb25
        x                   - legendre argument in [-1,1]
Packit 67cb25
        csphase             - -1.0 to include CS phase (-1)^m,
Packit 67cb25
                               1.0 to not include
Packit 67cb25
        result_array        - (output) where to store P_{lm}(x) values
Packit 67cb25
        result_deriv_array  - (output) where to store
Packit 67cb25
                              d/dtheta P_{lm}(x) values
Packit 67cb25
        result_deriv2_array - (output) where to store
Packit 67cb25
                              d^2/dtheta^2 P_{lm}(x) values
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
FUNCTION(legendre, array_none_e)
Packit 67cb25
(const size_t lmax, const double x, const double csphase, OUTPUT_ARG)
Packit 67cb25
{
Packit 67cb25
  if (x > 1.0 || x < -1.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("x is outside [-1,1]", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
#if defined(LEGENDRE_DERIV) || defined(LEGENDRE_DERIV2)
Packit 67cb25
  else if (fabs(x) == 1.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("x cannot equal 1 or -1 for derivative computation", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
#endif
Packit 67cb25
  else if (csphase != 1.0 && csphase != -1.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("csphase has invalid value", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      const double u = sqrt((1.0 - x) * (1.0 + x)); /* sin(theta) */
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      const double uinv = 1.0 / u;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      const double uinv2 = 1.0 / u / u;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV) || defined(LEGENDRE_DERIV2)
Packit 67cb25
      const double xbyu = x * uinv; /* x / u */
Packit 67cb25
#endif
Packit 67cb25
      size_t l, m;
Packit 67cb25
      size_t k, idxmm;
Packit 67cb25
      double plm, pmm;
Packit 67cb25
      double pm1,    /* P(l-1,m) */
Packit 67cb25
             pm2;    /* P(l-2,m) */
Packit 67cb25
      double twomm1; /* 2*m - 1 */
Packit 67cb25
Packit 67cb25
      /* initial values P(0,0) and P(1,0) */
Packit 67cb25
Packit 67cb25
      pm2 = 1.0; /* P(0,0) */
Packit 67cb25
      pm1 = x;   /* P(1,0) */
Packit 67cb25
Packit 67cb25
      result_array[0] = pm2;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      result_deriv_array[0] = 0.0;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      result_deriv2_array[0] = 0.0;
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
      if (lmax == 0)
Packit 67cb25
        return 0;
Packit 67cb25
Packit 67cb25
      result_array[1] = pm1;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      result_deriv_array[1] = -u;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      result_deriv2_array[1] = -x;
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
      /* Compute P(l,0) */
Packit 67cb25
Packit 67cb25
      k = 1;
Packit 67cb25
      for (l = 2; l <= lmax; ++l)
Packit 67cb25
        {
Packit 67cb25
          k += l;
Packit 67cb25
          plm = ((2*l - 1) * x * pm1 - (l - 1) * pm2) / (double) l;
Packit 67cb25
          result_array[k] = plm;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
          result_deriv_array[k] = -(double)l * (pm1 - x * plm) * uinv;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
          result_deriv2_array[k] = -(double) l * (l + 1.0) * plm -
Packit 67cb25
                                   xbyu * result_deriv_array[k];
Packit 67cb25
#endif
Packit 67cb25
          pm2 = pm1;
Packit 67cb25
          pm1 = plm;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* Compute P(m,m), P(m+1,m) and P(l,m) */
Packit 67cb25
Packit 67cb25
      pmm = 1.0;
Packit 67cb25
      twomm1 = -1.0; /* 2 * m - 1 */
Packit 67cb25
Packit 67cb25
      idxmm = 0; /* tracks idx(m,m), initialize to idx(0,0) */
Packit 67cb25
Packit 67cb25
      for (m = 1; m <= lmax - 1; ++m)
Packit 67cb25
        {
Packit 67cb25
          /*
Packit 67cb25
           * compute
Packit 67cb25
           *
Packit 67cb25
           * P(m,m) = u * (2m - 1) P(m-1,m-1)
Packit 67cb25
           * and
Packit 67cb25
           * dP(m,m)/dtheta = m * x * P(m,m) / u
Packit 67cb25
           */
Packit 67cb25
          idxmm += m + 1;
Packit 67cb25
          twomm1 += 2.0;
Packit 67cb25
          pmm *= csphase * u * twomm1;
Packit 67cb25
          result_array[idxmm] = pmm;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
          result_deriv_array[idxmm] = m * xbyu * pmm;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
          result_deriv2_array[idxmm] =
Packit 67cb25
            m * (uinv2 * m - (m + 1.0)) * result_array[idxmm] -
Packit 67cb25
            xbyu * result_deriv_array[idxmm];
Packit 67cb25
#endif
Packit 67cb25
          pm2 = pmm;
Packit 67cb25
Packit 67cb25
          /*
Packit 67cb25
           * compute
Packit 67cb25
           *
Packit 67cb25
           * P(m+1,m) = (2 * m + 1) * x * P(m,m)
Packit 67cb25
           * and
Packit 67cb25
           * dP(m+1,m)/dt = -[(2*m + 1) * P(m,m) - (m+1) * x * P(m+1,m)]/u
Packit 67cb25
           */
Packit 67cb25
          k = idxmm + m + 1;
Packit 67cb25
          pm1 = x * pmm * (2*m + 1);
Packit 67cb25
          result_array[k] = pm1;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
          result_deriv_array[k] = -uinv * ((2*m + 1) * pmm - (m + 1) * x * pm1);
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
          result_deriv2_array[k] =
Packit 67cb25
            (m * m * uinv2 - (m + 1.0) * (m + 2.0)) * result_array[k] -
Packit 67cb25
            xbyu * result_deriv_array[k];
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
          /* compute P(l,m) */
Packit 67cb25
          for (l = m + 2; l <= lmax; ++l)
Packit 67cb25
            {
Packit 67cb25
              k += l;
Packit 67cb25
              plm = ((2*l - 1) * x * pm1 - (l + m - 1) * pm2) /
Packit 67cb25
                    (double) (l - m);
Packit 67cb25
              result_array[k] = plm;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
              result_deriv_array[k] = -uinv * ((l + m) * pm1 - l * x * plm);
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
              result_deriv2_array[k] =
Packit 67cb25
                (m * m * uinv2 - l * (l + 1.0)) * result_array[k] -
Packit 67cb25
                xbyu * result_deriv_array[k];
Packit 67cb25
#endif
Packit 67cb25
              pm2 = pm1;
Packit 67cb25
              pm1 = plm;
Packit 67cb25
            }
Packit 67cb25
        } /* for (m = 1; m <= lmax - 1; ++m) */
Packit 67cb25
Packit 67cb25
      /* compute P(lmax,lmax) */
Packit 67cb25
Packit 67cb25
      idxmm += m + 1;
Packit 67cb25
      twomm1 += 2.0;
Packit 67cb25
      pmm *= csphase * u * twomm1;
Packit 67cb25
      result_array[idxmm] = pmm;
Packit 67cb25
#if defined(LEGENDRE_DERIV)
Packit 67cb25
      result_deriv_array[idxmm] = lmax * x * pmm * uinv;
Packit 67cb25
#endif
Packit 67cb25
#if defined(LEGENDRE_DERIV2)
Packit 67cb25
      result_deriv2_array[idxmm] =
Packit 67cb25
        lmax * (uinv2 * lmax - (lmax + 1.0)) * result_array[idxmm] -
Packit 67cb25
        xbyu * result_deriv_array[idxmm];
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* legendre_array_none_e() */
Packit 67cb25
Packit 67cb25
#undef FUNCTION
Packit 67cb25
#undef CONCAT2x
Packit 67cb25
#undef CONCAT3x
Packit 67cb25
#undef OUTPUT
Packit 67cb25
#undef OUTPUT_ARG
Packit 67cb25
#undef LEGENDRE_DERIV
Packit 67cb25
#undef LEGENDRE_DERIV2
Packit 67cb25
#undef LEGENDRE_DERIV_ALT