|
|
|
|
/* GSL - Generic Sound Layer
|
|
|
|
|
* Copyright (C) 2001 Stefan Westerfeld and Tim Janik
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library 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
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General
|
|
|
|
|
* Public License along with this library; if not, write to the
|
|
|
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
|
*/
|
|
|
|
|
#include "gslmath.h"
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define RING_BUFFER_LENGTH (16)
|
|
|
|
|
#define PRINTF_DIGITS "1270"
|
|
|
|
|
#define FLOAT_STRING_SIZE (2048)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* factorization constants: 2^(1/12), ln(2^(1/12)) and 2^(1/(12*6))
|
|
|
|
|
* retrived with:
|
|
|
|
|
#include <stl.h>
|
|
|
|
|
#include <complex.h>
|
|
|
|
|
typedef long double ld;
|
|
|
|
|
|
|
|
|
|
int main (void)
|
|
|
|
|
{
|
|
|
|
|
ld r, l;
|
|
|
|
|
|
|
|
|
|
cout.precision(256);
|
|
|
|
|
|
|
|
|
|
r = pow ((ld) 2, (ld) 1 / (ld) 12);
|
|
|
|
|
cout << "2^(1/12) =\n";
|
|
|
|
|
cout << "2^" << (ld) 1 / (ld) 12 << " =\n";
|
|
|
|
|
cout << r << "\n";
|
|
|
|
|
|
|
|
|
|
l = log (r);
|
|
|
|
|
cout << "ln(2^(1/12)) =\n";
|
|
|
|
|
cout << "ln(" << r << ") =\n";
|
|
|
|
|
cout << l << "\n";
|
|
|
|
|
|
|
|
|
|
r = pow ((ld) 2, (ld) 1 / (ld) 72);
|
|
|
|
|
cout << "2^(1/72) =\n";
|
|
|
|
|
cout << "2^" << (ld) 1 / (ld) 72 << " =\n";
|
|
|
|
|
cout << r << "\n";
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* --- prototypes --- */
|
|
|
|
|
static void zrhqr (double a[], int m, double rtr[], double rti[]);
|
|
|
|
|
static double rf (double x, double y, double z);
|
|
|
|
|
static double ellf (double phi, double ak);
|
|
|
|
|
static void sncndn (double uu, double emmc, double *sn_p, double *cn_p, double *dn_p);
|
|
|
|
|
static void sncndnC (GslComplex uu, GslComplex emmc, GslComplex *sn_p, GslComplex *cn_p, GslComplex *dn_p);
|
|
|
|
|
static GslComplex rfC (GslComplex x, GslComplex y, GslComplex z);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* --- functions --- */
|
|
|
|
|
static inline char*
|
|
|
|
|
pretty_print_double (char *str,
|
|
|
|
|
double d)
|
|
|
|
|
{
|
|
|
|
|
char *s= str;
|
|
|
|
|
|
|
|
|
|
sprintf (s, "%."PRINTF_DIGITS"f", d);
|
|
|
|
|
while (*s) s++;
|
|
|
|
|
while (s[-1] == '0' && s[-2] != '.')
|
|
|
|
|
s--;
|
|
|
|
|
*s = 0;
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char*
|
|
|
|
|
gsl_complex_list (unsigned int n_points,
|
|
|
|
|
GslComplex *points,
|
|
|
|
|
const char *indent)
|
|
|
|
|
{
|
|
|
|
|
static unsigned int rbi = 0;
|
|
|
|
|
static char* rbuffer[RING_BUFFER_LENGTH] = { NULL, };
|
|
|
|
|
char *s, *tbuffer = g_newa (char, (FLOAT_STRING_SIZE * 2 * n_points));
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
rbi++; if (rbi >= RING_BUFFER_LENGTH) rbi -= RING_BUFFER_LENGTH;
|
|
|
|
|
if (rbuffer[rbi] != NULL)
|
|
|
|
|
g_free (rbuffer[rbi]);
|
|
|
|
|
s = tbuffer;
|
|
|
|
|
for (i = 0; i < n_points; i++)
|
|
|
|
|
{
|
|
|
|
|
*s = 0;
|
|
|
|
|
if (indent)
|
|
|
|
|
strcat (s, indent);
|
|
|
|
|
while (*s) s++;
|
|
|
|
|
s = pretty_print_double (s, points[i].re);
|
|
|
|
|
*s++ = ' ';
|
|
|
|
|
s = pretty_print_double (s, points[i].im);
|
|
|
|
|
*s++ = '\n';
|
|
|
|
|
}
|
|
|
|
|
*s++ = 0;
|
|
|
|
|
rbuffer[rbi] = g_strdup (tbuffer);
|
|
|
|
|
return rbuffer[rbi];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char*
|
|
|
|
|
gsl_complex_str (GslComplex c)
|
|
|
|
|
{
|
|
|
|
|
static unsigned int rbi = 0;
|
|
|
|
|
static char* rbuffer[RING_BUFFER_LENGTH] = { NULL, };
|
|
|
|
|
char *s, tbuffer[FLOAT_STRING_SIZE * 2];
|
|
|
|
|
|
|
|
|
|
rbi++; if (rbi >= RING_BUFFER_LENGTH) rbi -= RING_BUFFER_LENGTH;
|
|
|
|
|
if (rbuffer[rbi] != NULL)
|
|
|
|
|
g_free (rbuffer[rbi]);
|
|
|
|
|
s = tbuffer;
|
|
|
|
|
*s++ = '{';
|
|
|
|
|
s = pretty_print_double (s, c.re);
|
|
|
|
|
*s++ = ',';
|
|
|
|
|
*s++ = ' ';
|
|
|
|
|
s = pretty_print_double (s, c.im);
|
|
|
|
|
*s++ = '}';
|
|
|
|
|
*s++ = 0;
|
|
|
|
|
rbuffer[rbi] = g_strdup (tbuffer);
|
|
|
|
|
return rbuffer[rbi];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char*
|
|
|
|
|
gsl_poly_str (unsigned int degree,
|
|
|
|
|
double *a,
|
|
|
|
|
const char *var)
|
|
|
|
|
{
|
|
|
|
|
static unsigned int rbi = 0;
|
|
|
|
|
static char* rbuffer[RING_BUFFER_LENGTH] = { NULL, };
|
|
|
|
|
char *s, *tbuffer = g_newa (char, degree * FLOAT_STRING_SIZE);
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
if (!var)
|
|
|
|
|
var = "x";
|
|
|
|
|
rbi++; if (rbi >= RING_BUFFER_LENGTH) rbi -= RING_BUFFER_LENGTH;
|
|
|
|
|
if (rbuffer[rbi] != NULL)
|
|
|
|
|
g_free (rbuffer[rbi]);
|
|
|
|
|
s = tbuffer;
|
|
|
|
|
*s++ = '(';
|
|
|
|
|
s = pretty_print_double (s, a[0]);
|
|
|
|
|
for (i = 1; i <= degree; i++)
|
|
|
|
|
{
|
|
|
|
|
*s++ = '+';
|
|
|
|
|
*s = 0; strcat (s, var); while (*s) s++;
|
|
|
|
|
*s++ = '*';
|
|
|
|
|
*s++ = '(';
|
|
|
|
|
s = pretty_print_double (s, a[i]);
|
|
|
|
|
}
|
|
|
|
|
while (i--)
|
|
|
|
|
*s++ = ')';
|
|
|
|
|
*s++ = 0;
|
|
|
|
|
rbuffer[rbi] = g_strdup (tbuffer);
|
|
|
|
|
return rbuffer[rbi];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char*
|
|
|
|
|
gsl_poly_str1 (unsigned int degree,
|
|
|
|
|
double *a,
|
|
|
|
|
const char *var)
|
|
|
|
|
{
|
|
|
|
|
static unsigned int rbi = 0;
|
|
|
|
|
static char* rbuffer[RING_BUFFER_LENGTH] = { NULL, };
|
|
|
|
|
char *s, *tbuffer = g_newa (char, degree * FLOAT_STRING_SIZE);
|
|
|
|
|
unsigned int i, need_plus = 0;
|
|
|
|
|
|
|
|
|
|
if (!var)
|
|
|
|
|
var = "x";
|
|
|
|
|
rbi++; if (rbi >= RING_BUFFER_LENGTH) rbi -= RING_BUFFER_LENGTH;
|
|
|
|
|
if (rbuffer[rbi] != NULL)
|
|
|
|
|
g_free (rbuffer[rbi]);
|
|
|
|
|
s = tbuffer;
|
|
|
|
|
*s++ = '(';
|
|
|
|
|
if (a[0] != 0.0)
|
|
|
|
|
{
|
|
|
|
|
s = pretty_print_double (s, a[0]);
|
|
|
|
|
need_plus = 1;
|
|
|
|
|
}
|
|
|
|
|
for (i = 1; i <= degree; i++)
|
|
|
|
|
{
|
|
|
|
|
if (a[i] == 0.0)
|
|
|
|
|
continue;
|
|
|
|
|
if (need_plus)
|
|
|
|
|
{
|
|
|
|
|
*s++ = ' ';
|
|
|
|
|
*s++ = '+';
|
|
|
|
|
*s++ = ' ';
|
|
|
|
|
}
|
|
|
|
|
if (a[i] != 1.0)
|
|
|
|
|
{
|
|
|
|
|
s = pretty_print_double (s, a[i]);
|
|
|
|
|
*s++ = '*';
|
|
|
|
|
}
|
|
|
|
|
*s = 0;
|
|
|
|
|
strcat (s, var);
|
|
|
|
|
while (*s) s++;
|
|
|
|
|
if (i > 1)
|
|
|
|
|
{
|
|
|
|
|
*s++ = '*';
|
|
|
|
|
*s++ = '*';
|
|
|
|
|
sprintf (s, "%u", i);
|
|
|
|
|
while (*s) s++;
|
|
|
|
|
}
|
|
|
|
|
need_plus = 1;
|
|
|
|
|
}
|
|
|
|
|
*s++ = ')';
|
|
|
|
|
*s++ = 0;
|
|
|
|
|
rbuffer[rbi] = g_strdup (tbuffer);
|
|
|
|
|
return rbuffer[rbi];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gsl_complex_gnuplot (const char *file_name,
|
|
|
|
|
unsigned int n_points,
|
|
|
|
|
GslComplex *points)
|
|
|
|
|
{
|
|
|
|
|
FILE *fout = fopen (file_name, "w");
|
|
|
|
|
|
|
|
|
|
fputs (gsl_complex_list (n_points, points, ""), fout);
|
|
|
|
|
fclose (fout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
gsl_temp_freq (double kammer_freq,
|
|
|
|
|
int halftone_delta)
|
|
|
|
|
{
|
|
|
|
|
double factor;
|
|
|
|
|
|
|
|
|
|
factor = pow (GSL_2_POW_1_DIV_12, halftone_delta);
|
|
|
|
|
|
|
|
|
|
return kammer_freq * factor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gsl_poly_from_re_roots (unsigned int degree,
|
|
|
|
|
double *a,
|
|
|
|
|
GslComplex *roots)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
/* initialize polynomial */
|
|
|
|
|
a[1] = 1;
|
|
|
|
|
a[0] = -roots[0].re;
|
|
|
|
|
/* monomial factor multiplication */
|
|
|
|
|
for (i = 1; i < degree; i++)
|
|
|
|
|
{
|
|
|
|
|
unsigned int j;
|
|
|
|
|
|
|
|
|
|
a[i + 1] = a[i];
|
|
|
|
|
for (j = i; j >= 1; j--)
|
|
|
|
|
a[j] = a[j - 1] - a[j] * roots[i].re;
|
|
|
|
|
a[0] *= -roots[i].re;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gsl_cpoly_from_roots (unsigned int degree,
|
|
|
|
|
GslComplex *c,
|
|
|
|
|
GslComplex *roots)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
/* initialize polynomial */
|
|
|
|
|
c[1].re = 1;
|
|
|
|
|
c[1].im = 0;
|
|
|
|
|
c[0].re = -roots[0].re;
|
|
|
|
|
c[0].im = -roots[0].im;
|
|
|
|
|
/* monomial factor multiplication */
|
|
|
|
|
for (i = 1; i < degree; i++)
|
|
|
|
|
{
|
|
|
|
|
GslComplex r = gsl_complex (-roots[i].re, -roots[i].im);
|
|
|
|
|
unsigned int j;
|
|
|
|
|
|
|
|
|
|
c[i + 1] = c[i];
|
|
|
|
|
for (j = i; j >= 1; j--)
|
|
|
|
|
c[j] = gsl_complex_add (c[j - 1], gsl_complex_mul (c[j], r));
|
|
|
|
|
c[0] = gsl_complex_mul (c[0], r);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gsl_poly_complex_roots (unsigned int degree,
|
|
|
|
|
double *a, /* [0..degree] (degree+1 elements) */
|
|
|
|
|
GslComplex *roots) /* [degree] */
|
|
|
|
|
{
|
|
|
|
|
double *roots_re = g_newa (double, 1 + degree);
|
|
|
|
|
double *roots_im = g_newa (double, 1 + degree);
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
zrhqr (a, degree, roots_re, roots_im);
|
|
|
|
|
for (i = 0; i < degree; i++)
|
|
|
|
|
{
|
|
|
|
|
roots[i].re = roots_re[1 + i];
|
|
|
|
|
roots[i].im = roots_im[1 + i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
gsl_ellip_rf (double x,
|
|
|
|
|
double y,
|
|
|
|
|
double z)
|
|
|
|
|
{
|
|
|
|
|
return rf (x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
gsl_ellip_F (double phi,
|
|
|
|
|
double ak)
|
|
|
|
|
{
|
|
|
|
|
return ellf (phi, ak);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
gsl_ellip_sn (double u,
|
|
|
|
|
double emmc)
|
|
|
|
|
{
|
|
|
|
|
double sn;
|
|
|
|
|
|
|
|
|
|
sncndn (u, emmc, &sn, NULL, NULL);
|
|
|
|
|
return sn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
gsl_ellip_asn (double y,
|
|
|
|
|
double emmc)
|
|
|
|
|
{
|
|
|
|
|
return y * rf (1.0 - y * y, 1.0 - y * y * (1.0 - emmc), 1.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GslComplex
|
|
|
|
|
gsl_complex_ellip_asn (GslComplex y,
|
|
|
|
|
GslComplex emmc)
|
|
|
|
|
{
|
|
|
|
|
return gsl_complex_mul (y,
|
|
|
|
|
rfC (gsl_complex_sub (gsl_complex (1.0, 0),
|
|
|
|
|
gsl_complex_mul (y, y)),
|
|
|
|
|
gsl_complex_sub (gsl_complex (1.0, 0),
|
|
|
|
|
gsl_complex_mul3 (y, y, gsl_complex_sub (gsl_complex (1.0, 0),
|
|
|
|
|
emmc))),
|
|
|
|
|
gsl_complex (1.0, 0)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GslComplex
|
|
|
|
|
gsl_complex_ellip_sn (GslComplex u,
|
|
|
|
|
GslComplex emmc)
|
|
|
|
|
{
|
|
|
|
|
GslComplex sn;
|
|
|
|
|
|
|
|
|
|
sncndnC (u, emmc, &sn, NULL, NULL);
|
|
|
|
|
return sn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
gsl_bit_depth_epsilon (guint n_bits)
|
|
|
|
|
{
|
|
|
|
|
/* epsilon for various bit depths, based on significance of one bit,
|
|
|
|
|
* minus fudge. created with:
|
|
|
|
|
* { echo "scale=40"; for i in `seq 1 32` ; do echo "1/2^$i - 10^-($i+1)" ; done } | bc | sed 's/$/,/'
|
|
|
|
|
*/
|
|
|
|
|
static const double bit_epsilons[] = {
|
|
|
|
|
.4900000000000000000000000000000000000000,
|
|
|
|
|
.2490000000000000000000000000000000000000,
|
|
|
|
|
.1249000000000000000000000000000000000000,
|
|
|
|
|
.0624900000000000000000000000000000000000,
|
|
|
|
|
.0312490000000000000000000000000000000000,
|
|
|
|
|
.0156249000000000000000000000000000000000,
|
|
|
|
|
.0078124900000000000000000000000000000000,
|
|
|
|
|
.0039062490000000000000000000000000000000,
|
|
|
|
|
.0019531249000000000000000000000000000000,
|
|
|
|
|
.0009765624900000000000000000000000000000,
|
|
|
|
|
.0004882812490000000000000000000000000000,
|
|
|
|
|
.0002441406249000000000000000000000000000,
|
|
|
|
|
.0001220703124900000000000000000000000000,
|
|
|
|
|
.0000610351562490000000000000000000000000,
|
|
|
|
|
.0000305175781249000000000000000000000000,
|
|
|
|
|
.0000152587890624900000000000000000000000,
|
|
|
|
|
.0000076293945312490000000000000000000000,
|
|
|
|
|
.0000038146972656249000000000000000000000,
|
|
|
|
|
.0000019073486328124900000000000000000000,
|
|
|
|
|
.0000009536743164062490000000000000000000,
|
|
|
|
|
.0000004768371582031249000000000000000000,
|
|
|
|
|
.0000002384185791015624900000000000000000,
|
|
|
|
|
.0000001192092895507812490000000000000000,
|
|
|
|
|
.0000000596046447753906249000000000000000,
|
|
|
|
|
.0000000298023223876953124900000000000000,
|
|
|
|
|
.0000000149011611938476562490000000000000,
|
|
|
|
|
.0000000074505805969238281249000000000000,
|
|
|
|
|
.0000000037252902984619140624900000000000,
|
|
|
|
|
.0000000018626451492309570312490000000000,
|
|
|
|
|
.0000000009313225746154785156249000000000,
|
|
|
|
|
.0000000004656612873077392578124900000000,
|
|
|
|
|
.0000000002328306436538696289062490000000,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return bit_epsilons[CLAMP (n_bits, 1, 32) - 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* --- Numerical Receipes --- */
|
|
|
|
|
#define gsl_complex_rmul(scale, c) gsl_complex_scale (c, scale)
|
|
|
|
|
#define ONE gsl_complex (1.0, 0)
|
|
|
|
|
#define SIGN(a,b) ((b) >= 0.0 ? fabs (a) : -fabs(a))
|
|
|
|
|
static inline int IMAX (int i1, int i2) { return i1 > i2 ? i1 : i2; }
|
|
|
|
|
static inline double DMIN (double d1, double d2) { return d1 < d2 ? d1 : d2; }
|
|
|
|
|
static inline double DMAX (double d1, double d2) { return d1 > d2 ? d1 : d2; }
|
|
|
|
|
static inline double DSQR (double d) { return d == 0.0 ? 0.0 : d * d; }
|
|
|
|
|
#define nrerror(error) g_error ("NR-ERROR: %s", (error))
|
|
|
|
|
static inline double* vector (long nl, long nh)
|
|
|
|
|
/* allocate a vector with subscript range v[nl..nh] */
|
|
|
|
|
{
|
|
|
|
|
double *v = g_new (double, nh - nl + 1 + 1);
|
|
|
|
|
return v - nl + 1;
|
|
|
|
|
}
|
|
|
|
|
static inline void free_vector (double *v, long nl, long nh)
|
|
|
|
|
{
|
|
|
|
|
g_free (v + nl - 1);
|
|
|
|
|
}
|
|
|
|
|
static inline double** matrix (long nrl, long nrh, long ncl, long nch)
|
|
|
|
|
/* allocate a matrix with subscript range m[nrl..nrh][ncl..nch] */
|
|
|
|
|
{
|
|
|
|
|
long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
|
|
|
|
|
double **m = g_new (double*, nrow + 1);
|
|
|
|
|
m += 1;
|
|
|
|
|
m -= nrl;
|
|
|
|
|
m[nrl] = g_new (double, nrow * ncol + 1);
|
|
|
|
|
m[nrl] += 1;
|
|
|
|
|
m[nrl] -= ncl;
|
|
|
|
|
for (i = nrl + 1; i <= nrh; i++)
|
|
|
|
|
m[i] = m[i - 1] + ncol;
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
static inline void free_matrix (double **m, long nrl, long nrh, long ncl, long nch)
|
|
|
|
|
{
|
|
|
|
|
g_free (m[nrl] + ncl - 1);
|
|
|
|
|
g_free (m + nrl - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
poldiv (double u[], int n, double v[], int nv, double q[], double r[])
|
|
|
|
|
/* Given the n+1 coefficients of a polynomial of degree n in u[0..n], and the nv+1 coefficients
|
|
|
|
|
of another polynomial of degree nv in v[0..nv], divide the polynomial u by the polynomial
|
|
|
|
|
v ("u"/"v") giving a quotient polynomial whose coefficients are returned in q[0..n], and a
|
|
|
|
|
remainder polynomial whose coefficients are returned in r[0..n]. The elements r[nv..n]
|
|
|
|
|
and q[n-nv+1..n] are returned as zero. */
|
|
|
|
|
{
|
|
|
|
|
int k,j;
|
|
|
|
|
|
|
|
|
|
for (j=0;j<=n;j++) {
|
|
|
|
|
r[j]=u[j];
|
|
|
|
|
q[j]=0.0;
|
|
|
|
|
}for (k=n-nv;k>=0;k--) {
|
|
|
|
|
q[k]=r[nv+k]/v[nv];
|
|
|
|
|
for (j=nv+k-1;j>=k;j--) r[j] -= q[k]*v[j-k];
|
|
|
|
|
}for (j=nv;j<=n;j++) r[j]=0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MAX_ITER_BASE 9 /* TIMJ: was 3 */
|
|
|
|
|
#define MAX_ITER_FAC 20 /* TIMJ: was 10 */
|
|
|
|
|
static void
|
|
|
|
|
hqr (double **a, int n, double wr[], double wi[])
|
|
|
|
|
/* Finds all eigenvalues of an upper Hessenberg matrix a[1..n][1..n]. On input a can be
|
|
|
|
|
exactly as output from elmhes <EFBFBD>11.5; on output it is destroyed. The real and imaginary parts
|
|
|
|
|
of the eigenvalues are returned in wr[1..n] and wi[1..n], respectively. */
|
|
|
|
|
{
|
|
|
|
|
int nn,m,l,k,j,its,i,mmin;
|
|
|
|
|
double z,y,x,w,v,u,t,s,r,q,p,anorm;
|
|
|
|
|
r=q=p=0; /* TIMJ: silence compiler */
|
|
|
|
|
|
|
|
|
|
anorm=0.0; /* Compute matrix norm for possible use in lo- */
|
|
|
|
|
for (i=1;i<=n;i++) /* cating single small subdiagonal element. */
|
|
|
|
|
for (j=IMAX (i-1,1);j<=n;j++)
|
|
|
|
|
anorm += fabs (a[i][j]);
|
|
|
|
|
nn=n;
|
|
|
|
|
t=0.0; /* Gets changed only by an exceptional shift. */
|
|
|
|
|
while (nn >= 1) { /* Begin search for next eigenvalue. */
|
|
|
|
|
its=0;
|
|
|
|
|
do {for (l=nn;l>=2;l--) { /* Begin iteration: look for single small subdi- */
|
|
|
|
|
s=fabs (a[l-1][l-1])+fabs (a[l][l]); /* agonal element. */
|
|
|
|
|
if (s == 0.0) s=anorm;
|
|
|
|
|
if ((double)(fabs (a[l][l-1]) + s) == s) break;
|
|
|
|
|
}
|
|
|
|
|
x=a[nn][nn];
|
|
|
|
|
if (l == nn) { /* One root found. */
|
|
|
|
|
wr[nn]=x+t;
|
|
|
|
|
wi[nn--]=0.0;
|
|
|
|
|
} else {
|
|
|
|
|
y=a[nn-1][nn-1];
|
|
|
|
|
w=a[nn][nn-1]*a[nn-1][nn];
|
|
|
|
|
if (l == (nn-1)) { /* Two roots found... */
|
|
|
|
|
p=0.5*(y-x);
|
|
|
|
|
q=p*p+w;
|
|
|
|
|
z=sqrt (fabs (q));
|
|
|
|
|
x += t;
|
|
|
|
|
if (q >= 0.0) { /* ...a real pair. */
|
|
|
|
|
z=p+SIGN (z,p);
|
|
|
|
|
wr[nn-1]=wr[nn]=x+z;
|
|
|
|
|
if (z) wr[nn]=x-w/z;
|
|
|
|
|
wi[nn-1]=wi[nn]=0.0;
|
|
|
|
|
} else { /* ...a complex pair. */
|
|
|
|
|
wr[nn-1]=wr[nn]=x+p;
|
|
|
|
|
wi[nn-1]= -(wi[nn]=z);
|
|
|
|
|
}
|
|
|
|
|
nn -= 2;
|
|
|
|
|
} else { /* No roots found. Continue iteration. */
|
|
|
|
|
if (its == MAX_ITER_BASE * MAX_ITER_FAC)
|
|
|
|
|
nrerror ("Too many iterations in hqr");
|
|
|
|
|
if (its && !(its%MAX_ITER_FAC)) { /* Form exceptional shift. */
|
|
|
|
|
t += x;
|
|
|
|
|
for (i=1;i<=nn;i++) a[i][i] -= x;
|
|
|
|
|
s=fabs (a[nn][nn-1])+fabs (a[nn-1][nn-2]);
|
|
|
|
|
y=x=0.75*s;
|
|
|
|
|
w = -0.4375*s*s;
|
|
|
|
|
}
|
|
|
|
|
++its;
|
|
|
|
|
for (m=(nn-2);m>=l;m--) { /* Form shift and then look for */
|
|
|
|
|
z=a[m][m]; /* 2 consecutive small sub- */
|
|
|
|
|
r=x-z; /* diagonal elements. */
|
|
|
|
|
s=y-z;
|
|
|
|
|
p=(r*s-w)/a[m+1][m]+a[m][m+1]; /* Equation (11.6.23). */
|
|
|
|
|
q=a[m+1][m+1]-z-r-s;
|
|
|
|
|
r=a[m+2][m+1];
|
|
|
|
|
s=fabs (p)+fabs (q)+fabs (r); /* Scale to prevent overflow or */
|
|
|
|
|
p /= s; /* underflow. */
|
|
|
|
|
q /= s;
|
|
|
|
|
r /= s;
|
|
|
|
|
if (m == l) break;
|
|
|
|
|
u=fabs (a[m][m-1])*(fabs (q)+fabs (r));
|
|
|
|
|
v=fabs (p)*(fabs (a[m-1][m-1])+fabs (z)+fabs (a[m+1][m+1]));
|
|
|
|
|
if ((double)(u+v) == v)
|
|
|
|
|
break; /* Equation (11.6.26). */
|
|
|
|
|
}
|
|
|
|
|
for (i=m+2;i<=nn;i++) {
|
|
|
|
|
a[i][i-2]=0.0;
|
|
|
|
|
if (i != (m+2))
|
|
|
|
|
a[i][i-3]=0.0;
|
|
|
|
|
}
|
|
|
|
|
for (k=m;k<=nn-1;k++) {
|
|
|
|
|
/* Double QR step on rows l to nn and columns m to nn. */
|
|
|
|
|
if (k != m) {
|
|
|
|
|
p=a[k][k-1]; /* Begin setup of Householder */
|
|
|
|
|
q=a[k+1][k-1]; /* vector. */
|
|
|
|
|
r=0.0;
|
|
|
|
|
if (k != (nn-1)) r=a[k+2][k-1];
|
|
|
|
|
if ((x=fabs (p)+fabs (q)+fabs (r)) != 0.0) {
|
|
|
|
|
p /= x; /* Scale to prevent overflow or */
|
|
|
|
|
q /= x; /* underflow. */
|
|
|
|
|
r /= x;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((s=SIGN (sqrt (p*p+q*q+r*r),p)) != 0.0) {
|
|
|
|
|
if (k == m) {
|
|
|
|
|
if (l != m)
|
|
|
|
|
a[k][k-1] = -a[k][k-1];
|
|
|
|
|
} else
|
|
|
|
|
a[k][k-1] = -s*x;
|
|
|
|
|
p += s; /* Equations (11.6.24). */
|
|
|
|
|
x=p/s;
|
|
|
|
|
y=q/s;
|
|
|
|
|
z=r/s;
|
|
|
|
|
q /= p;
|
|
|
|
|
r /= p;
|
|
|
|
|
for (j=k;j<=nn;j++) { /* Row modification. */
|
|
|
|
|
p=a[k][j]+q*a[k+1][j];
|
|
|
|
|
if (k != (nn-1)) {
|
|
|
|
|
p += r*a[k+2][j];
|
|
|
|
|
a[k+2][j] -= p*z;
|
|
|
|
|
}
|
|
|
|
|
a[k+1][j] -= p*y;
|
|
|
|
|
a[k][j] -= p*x;
|
|
|
|
|
}
|
|
|
|
|
mmin = nn<k+3 ? nn : k+3;
|
|
|
|
|
for (i=l;i<=mmin;i++) { /* Column modification. */
|
|
|
|
|
p=x*a[i][k]+y*a[i][k+1];
|
|
|
|
|
if (k != (nn-1)) {
|
|
|
|
|
p += z*a[i][k+2];
|
|
|
|
|
a[i][k+2] -= p*r;
|
|
|
|
|
}a[i][k+1] -= p*q;
|
|
|
|
|
a[i][k] -= p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (l < nn-1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define RADIX 2.0
|
|
|
|
|
static void
|
|
|
|
|
balanc (double **a, int n)
|
|
|
|
|
/* Given a matrix a[1..n][1..n], this routine tqreplaces it by a balanced matrix with identical
|
|
|
|
|
eigenvalues. A symmetric matrix is already balanced and is unaffected by this procedure. The
|
|
|
|
|
parameter RADIX should be the machine's floating-point radix. */
|
|
|
|
|
{
|
|
|
|
|
int last,j,i;
|
|
|
|
|
double s,r,g,f,c,sqrdx;
|
|
|
|
|
|
|
|
|
|
sqrdx=RADIX*RADIX;
|
|
|
|
|
last=0;
|
|
|
|
|
while (last == 0) {
|
|
|
|
|
last=1;
|
|
|
|
|
for (i=1;i<=n;i++) { /* Calculate row and column norms. */
|
|
|
|
|
r=c=0.0;
|
|
|
|
|
for (j=1;j<=n;j++)
|
|
|
|
|
if (j != i) {
|
|
|
|
|
c += fabs (a[j][i]);
|
|
|
|
|
r += fabs (a[i][j]);
|
|
|
|
|
}
|
|
|
|
|
if (c && r) { /* If both are nonzero, */
|
|
|
|
|
g=r/RADIX;
|
|
|
|
|
f=1.0;
|
|
|
|
|
s=c+r;
|
|
|
|
|
while (c<g) { /* find the integer power of the machine radix that */
|
|
|
|
|
f *= RADIX; /* comes closest to balancing the matrix. */
|
|
|
|
|
c *= sqrdx;
|
|
|
|
|
}
|
|
|
|
|
g=r*RADIX;
|
|
|
|
|
while (c>g) {
|
|
|
|
|
f /= RADIX;
|
|
|
|
|
c /= sqrdx;
|
|
|
|
|
}
|
|
|
|
|
if ((c+r)/f < 0.95*s) {
|
|
|
|
|
last=0;
|
|
|
|
|
g=1.0/f;
|
|
|
|
|
for (j=1;j<=n;j++)
|
|
|
|
|
a[i][j] *= g; /* Apply similarity transformation */
|
|
|
|
|
for (j=1;j<=n;j++) a[j][i] *= f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MAX_DEGREE 50
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
zrhqr (double a[], int m, double rtr[], double rti[])
|
|
|
|
|
/* Find all the roots of a polynomial with real coefficients, E(i=0..m) a(i)x^i, given the degree m
|
|
|
|
|
and the coefficients a[0..m]. The method is to construct an upper Hessenberg matrix whose
|
|
|
|
|
eigenvalues are the desired roots, and then use the routines balanc and hqr. The real and
|
|
|
|
|
imaginary parts of the roots are returned in rtr[1..m] and rti[1..m], respectively. */
|
|
|
|
|
{
|
|
|
|
|
int j,k;
|
|
|
|
|
double **hess,xr,xi;
|
|
|
|
|
|
|
|
|
|
hess=matrix (1,MAX_DEGREE,1,MAX_DEGREE);
|
|
|
|
|
if (m > MAX_DEGREE || a[m] == 0.0 || /* TIMJ: */ fabs (a[m]) < 1e-15 )
|
|
|
|
|
nrerror ("bad args in zrhqr");
|
|
|
|
|
for (k=1;k<=m;k++) /* Construct the matrix. */
|
|
|
|
|
{
|
|
|
|
|
hess[1][k] = -a[m-k]/a[m];
|
|
|
|
|
for (j=2;j<=m;j++)
|
|
|
|
|
hess[j][k]=0.0;
|
|
|
|
|
if (k != m)
|
|
|
|
|
hess[k+1][k]=1.0;
|
|
|
|
|
}
|
|
|
|
|
balanc (hess,m); /* Find its eigenvalues. */
|
|
|
|
|
hqr (hess,m,rtr,rti);
|
|
|
|
|
if (0) /* TIMJ: don't need sorting */
|
|
|
|
|
for (j=2;j<=m;j++)
|
|
|
|
|
{ /* Sort roots by their real parts by straight insertion. */
|
|
|
|
|
xr=rtr[j];
|
|
|
|
|
xi=rti[j];
|
|
|
|
|
for (k=j-1;k>=1;k--)
|
|
|
|
|
{
|
|
|
|
|
if (rtr[k] <= xr)
|
|
|
|
|
break;
|
|
|
|
|
rtr[k+1]=rtr[k];
|
|
|
|
|
rti[k+1]=rti[k];
|
|
|
|
|
}
|
|
|
|
|
rtr[k+1]=xr;
|
|
|
|
|
rti[k+1]=xi;
|
|
|
|
|
}
|
|
|
|
|
free_matrix (hess,1,MAX_DEGREE,1,MAX_DEGREE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define EPSS 2.0e-16 /* TIMJ, was(float): 1.0e-7 */
|
|
|
|
|
#define MR 8
|
|
|
|
|
#define MT 100 /* TIMJ: was: 10 */
|
|
|
|
|
#define MAXIT (MT*MR)
|
|
|
|
|
/* Here EPSS is the estimated fractional roundoff error. We try to break (rare) limit cycles with
|
|
|
|
|
MR different fractional values, once every MT steps, for MAXIT total allowed iterations. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
laguer (GslComplex a[], int m, GslComplex *x, int *its)
|
|
|
|
|
/* Given the degree m and the m+1 complex coefficients a[0..m] of the polynomial mi=0 a[i]xi,
|
|
|
|
|
and given a complex value x, this routine improves x by Laguerre's method until it converges,
|
|
|
|
|
within the achievable roundoff limit, to a root of the given polynomial. The number of iterations
|
|
|
|
|
taken is returned as its. */
|
|
|
|
|
{
|
|
|
|
|
int iter,j;
|
|
|
|
|
double abx,abp,abm,err;
|
|
|
|
|
GslComplex dx,x1,b,d,f,g,h,sq,gp,gm,g2;
|
|
|
|
|
static double frac[MR+1] = {0.0,0.5,0.25,0.75,0.13,0.38,0.62,0.88,1.0};
|
|
|
|
|
/* Fractions used to break a limit cycle. */
|
|
|
|
|
|
|
|
|
|
for (iter=1;iter<=MAXIT;iter++)
|
|
|
|
|
{ /* Loop over iterations up to allowed maximum. */
|
|
|
|
|
*its=iter;
|
|
|
|
|
b=a[m];
|
|
|
|
|
err=gsl_complex_abs (b);
|
|
|
|
|
d=f=gsl_complex (0.0,0.0);
|
|
|
|
|
abx=gsl_complex_abs (*x);
|
|
|
|
|
for (j=m-1;j>=0;j--)
|
|
|
|
|
{ /* Efficient computation of the polynomial and */
|
|
|
|
|
f=gsl_complex_add (gsl_complex_mul (*x,f),d); /* its first two derivatives. */
|
|
|
|
|
d=gsl_complex_add (gsl_complex_mul (*x,d),b);
|
|
|
|
|
b=gsl_complex_add (gsl_complex_mul (*x,b),a[j]);
|
|
|
|
|
err=gsl_complex_abs (b)+abx*err;
|
|
|
|
|
}
|
|
|
|
|
err *= EPSS;
|
|
|
|
|
/* Estimate of roundoff error in evaluating polynomial. */
|
|
|
|
|
if (gsl_complex_abs (b) <= err)
|
|
|
|
|
return; /* We are on the root. */
|
|
|
|
|
g=gsl_complex_div (d,b); /* The generic case: use Laguerre's formula. */
|
|
|
|
|
g2=gsl_complex_mul (g,g);
|
|
|
|
|
h=gsl_complex_sub (g2,gsl_complex_rmul (2.0,gsl_complex_div (f,b)));
|
|
|
|
|
sq=gsl_complex_sqrt (gsl_complex_rmul ((double) (m-1),gsl_complex_sub (gsl_complex_rmul ((double) m,h),g2)));
|
|
|
|
|
gp=gsl_complex_add (g,sq);
|
|
|
|
|
gm=gsl_complex_sub (g,sq);
|
|
|
|
|
abp=gsl_complex_abs (gp);
|
|
|
|
|
abm=gsl_complex_abs (gm);
|
|
|
|
|
if (abp < abm)
|
|
|
|
|
gp=gm;
|
|
|
|
|
dx=((DMAX (abp,abm) > 0.0 ? gsl_complex_div (gsl_complex ((double) m,0.0),gp)
|
|
|
|
|
: gsl_complex_rmul (1+abx,gsl_complex (cos ((double)iter),sin ((double)iter)))));
|
|
|
|
|
x1=gsl_complex_sub (*x,dx);
|
|
|
|
|
if (x->re == x1.re && x->im == x1.im)
|
|
|
|
|
return; /* Converged. */
|
|
|
|
|
if (iter % MT) *x=x1;
|
|
|
|
|
else *x=gsl_complex_sub (*x,gsl_complex_rmul (frac[iter/MT],dx));
|
|
|
|
|
/* Every so often we take a fractional step, to break any limit cycle (itself a rare occurrence). */
|
|
|
|
|
}
|
|
|
|
|
nrerror ("too many iterations in laguer");
|
|
|
|
|
/* Very unusual - can occur only for complex roots. Try a different starting guess for the root. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Here is a driver routine that calls laguer in succession for each root, performs
|
|
|
|
|
the deflation, optionally polishes the roots by the same Laguerre method - if you
|
|
|
|
|
are not going to polish in some other way - and finally sorts the roots by their real
|
|
|
|
|
parts. (We will use this routine in Chapter 13.) */
|
|
|
|
|
|
|
|
|
|
#define EPS 4.0e-15 /* TIMJ, was(float): 2.0e-6 */
|
|
|
|
|
#define MAXM 100
|
|
|
|
|
/* A small number, and maximum anticipated value of m. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
zroots (GslComplex a[], int m, GslComplex roots[], int polish)
|
|
|
|
|
/* Given the degree m and the m+1 complex coefficients a[0..m] of the polynomial mi=0 a (i)xi,
|
|
|
|
|
this routine successively calls laguer and finds all m complex roots in roots[1..m]. The
|
|
|
|
|
boolean variable polish should be input as true (1) if polishing (also by Laguerre's method)
|
|
|
|
|
is desired, false (0) if the roots will be subsequently polished by other means. */
|
|
|
|
|
{
|
|
|
|
|
int i,its,j,jj;
|
|
|
|
|
GslComplex x,b,c,ad[MAXM];
|
|
|
|
|
|
|
|
|
|
for (j=0;j<=m;j++) ad[j]=a[j]; /* Copy of coefficients for successive deflation. */
|
|
|
|
|
for (j=m;j>=1;j--) /* Loop over each root to be found. */
|
|
|
|
|
{
|
|
|
|
|
x=gsl_complex (0.0,0.0); /* Start at zero to favor convergence to small- */
|
|
|
|
|
laguer (ad,j,&x,&its); /* est remaining root, and find the root. */
|
|
|
|
|
if (fabs (x.im) <= 2.0*EPS*fabs (x.re))
|
|
|
|
|
x.im=0.0;
|
|
|
|
|
roots[j]=x;
|
|
|
|
|
b=ad[j]; /* Forward deflation. */
|
|
|
|
|
for (jj=j-1;jj>=0;jj--)
|
|
|
|
|
{
|
|
|
|
|
c=ad[jj];
|
|
|
|
|
ad[jj]=b;
|
|
|
|
|
b=gsl_complex_add (gsl_complex_mul (x,b),c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (polish)
|
|
|
|
|
for (j=1;j<=m;j++) /* Polish the roots using the undeflated coeffi- */
|
|
|
|
|
laguer (a,m,&roots[j],&its); /* cients. */
|
|
|
|
|
for (j=2;j<=m;j++) /* Sort roots by their real parts by straight insertion */
|
|
|
|
|
{
|
|
|
|
|
x=roots[j];
|
|
|
|
|
for (i=j-1;i>=1;i--) {
|
|
|
|
|
if (roots[i].re <= x.re)
|
|
|
|
|
break;
|
|
|
|
|
roots[i+1]=roots[i];
|
|
|
|
|
}
|
|
|
|
|
roots[i+1]=x;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define ITMAX 20 /* At most ITMAX iterations. */
|
|
|
|
|
#define TINY 2.0-15 /* TIMJ, was (float): 1.0e-6 */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
qroot (double p[], int n, double *b, double *c, double eps)
|
|
|
|
|
/* Given n+1 coefficients p[0..n] of a polynomial of degree n, and trial values for the coefficients
|
|
|
|
|
of a quadratic factor x*x+b*x+c, improve the solution until the coefficients b,c change by less
|
|
|
|
|
than eps. The routine poldiv <EFBFBD>5.3 is used. */
|
|
|
|
|
{
|
|
|
|
|
int iter;
|
|
|
|
|
double sc,sb,s,rc,rb,r,dv,delc,delb;
|
|
|
|
|
double *q,*qq,*rem;
|
|
|
|
|
double d[3];
|
|
|
|
|
|
|
|
|
|
q=vector (0,n);
|
|
|
|
|
qq=vector (0,n);
|
|
|
|
|
rem=vector (0,n);
|
|
|
|
|
d[2]=1.0;
|
|
|
|
|
for (iter=1;iter<=ITMAX;iter++)
|
|
|
|
|
{
|
|
|
|
|
d[1]=(*b);
|
|
|
|
|
d[0]=(*c);
|
|
|
|
|
poldiv (p,n,d,2,q,rem);
|
|
|
|
|
s=rem[0]; /* First division r,s. */
|
|
|
|
|
r=rem[1];
|
|
|
|
|
poldiv (q,(n-1),d,2,qq,rem);
|
|
|
|
|
sb = -(*c)*(rc = -rem[1]); /* Second division partial r,s with respect to */
|
|
|
|
|
rb = -(*b)*rc+(sc = -rem[0]); /* c. */
|
|
|
|
|
dv=1.0/(sb*rc-sc*rb); /* Solve 2x2 equation. */
|
|
|
|
|
delb=(r*sc-s*rc)*dv;
|
|
|
|
|
delc=(-r*sb+s*rb)*dv;
|
|
|
|
|
*b += (delb=(r*sc-s*rc)*dv);
|
|
|
|
|
*c += (delc=(-r*sb+s*rb)*dv);
|
|
|
|
|
if ((fabs (delb) <= eps*fabs (*b) || fabs (*b) < TINY)
|
|
|
|
|
&& (fabs (delc) <= eps*fabs (*c) || fabs (*c) < TINY))
|
|
|
|
|
{
|
|
|
|
|
free_vector (rem,0,n); /* Coefficients converged. */
|
|
|
|
|
free_vector (qq,0,n);
|
|
|
|
|
free_vector (q,0,n);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nrerror ("Too many iterations in routine qroot");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SNCNDN_CA 0.0003 /* The accuracy is the square of SNCNDN_CA. */
|
|
|
|
|
static void
|
|
|
|
|
sncndn (double uu, double emmc, double *sn_p, double *cn_p, double *dn_p)
|
|
|
|
|
/* Returns the Jacobian elliptic functions sn(u, kc), cn(u, kc), and dn(u, kc). Here uu = u, while
|
|
|
|
|
emmc = k2c. */
|
|
|
|
|
{
|
|
|
|
|
double a,b,c,d,emc,u,sn,cn,dn;
|
|
|
|
|
double em[14],en[14];
|
|
|
|
|
int i,ii,l,bo;
|
|
|
|
|
d=0; /* TIMJ: shutup compiler */
|
|
|
|
|
|
|
|
|
|
emc=emmc;
|
|
|
|
|
u=uu;
|
|
|
|
|
if (emc) {
|
|
|
|
|
bo=(emc < 0.0);
|
|
|
|
|
if (bo) {
|
|
|
|
|
d=1.0-emc;
|
|
|
|
|
emc /= -1.0/d;
|
|
|
|
|
u *= (d=sqrt(d));
|
|
|
|
|
}a=1.0;
|
|
|
|
|
dn=1.0;
|
|
|
|
|
for (i=1;i<=13;i++) {
|
|
|
|
|
l=i;
|
|
|
|
|
em[i]=a;
|
|
|
|
|
en[i]=(emc=sqrt(emc));
|
|
|
|
|
c=0.5*(a+emc);
|
|
|
|
|
if (fabs(a-emc) <= SNCNDN_CA*a) break;
|
|
|
|
|
emc *= a;
|
|
|
|
|
a=c;
|
|
|
|
|
}u *= c;
|
|
|
|
|
sn=sin(u);
|
|
|
|
|
cn=cos(u);
|
|
|
|
|
if (sn) {
|
|
|
|
|
a=cn/sn;
|
|
|
|
|
c *= a;
|
|
|
|
|
for (ii=l;ii>=1;ii--) {
|
|
|
|
|
b=em[ii];
|
|
|
|
|
a *= c;
|
|
|
|
|
c *= dn;
|
|
|
|
|
dn=(en[ii]+a)/(b+a);
|
|
|
|
|
a=c/b;
|
|
|
|
|
}a=1.0/sqrt(c*c+1.0);
|
|
|
|
|
sn=(sn >= 0.0 ? a : -a);
|
|
|
|
|
cn=c*sn;
|
|
|
|
|
}if (bo) {
|
|
|
|
|
a=dn;
|
|
|
|
|
dn=cn;
|
|
|
|
|
cn=a;
|
|
|
|
|
sn /= d;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cn=1.0/cosh(u);
|
|
|
|
|
dn=cn;
|
|
|
|
|
sn=tanh(u);
|
|
|
|
|
}
|
|
|
|
|
if (sn_p)
|
|
|
|
|
*sn_p = sn;
|
|
|
|
|
if (cn_p)
|
|
|
|
|
*cn_p = cn;
|
|
|
|
|
if (dn_p)
|
|
|
|
|
*dn_p = dn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
sncndnC (GslComplex uu, GslComplex emmc, GslComplex *sn_p, GslComplex *cn_p, GslComplex *dn_p)
|
|
|
|
|
{
|
|
|
|
|
GslComplex a,b,c,d,emc,u,sn,cn,dn;
|
|
|
|
|
GslComplex em[14],en[14];
|
|
|
|
|
int i,ii,l,bo;
|
|
|
|
|
|
|
|
|
|
emc=emmc;
|
|
|
|
|
u=uu;
|
|
|
|
|
if (emc.re || emc.im) /* gsl_complex_abs (emc)) */
|
|
|
|
|
{
|
|
|
|
|
/* bo=gsl_complex_abs (emc) < 0.0; */
|
|
|
|
|
bo=emc.re < 0.0;
|
|
|
|
|
if (bo) {
|
|
|
|
|
d=gsl_complex_sub (ONE, emc);
|
|
|
|
|
emc = gsl_complex_div (emc, gsl_complex_div (gsl_complex (-1.0, 0), d));
|
|
|
|
|
d = gsl_complex_sqrt (d);
|
|
|
|
|
u = gsl_complex_mul (u, d);
|
|
|
|
|
}
|
|
|
|
|
a=ONE; dn=ONE;
|
|
|
|
|
for (i=1;i<=13;i++) {
|
|
|
|
|
l=i;
|
|
|
|
|
em[i]=a;
|
|
|
|
|
emc = gsl_complex_sqrt (emc);
|
|
|
|
|
en[i]=emc;
|
|
|
|
|
c = gsl_complex_mul (gsl_complex (0.5, 0), gsl_complex_add (a, emc));
|
|
|
|
|
if (gsl_complex_abs (gsl_complex_sub (a, emc)) <=
|
|
|
|
|
gsl_complex_abs (gsl_complex_mul (gsl_complex (SNCNDN_CA, 0), a)))
|
|
|
|
|
break;
|
|
|
|
|
emc = gsl_complex_mul (emc, a);
|
|
|
|
|
a=c;
|
|
|
|
|
}
|
|
|
|
|
u = gsl_complex_mul (u, c);
|
|
|
|
|
sn = gsl_complex_sin (u);
|
|
|
|
|
cn = gsl_complex_cos (u);
|
|
|
|
|
if (sn.re) /* gsl_complex_abs (sn)) */
|
|
|
|
|
{
|
|
|
|
|
a= gsl_complex_div (cn, sn);
|
|
|
|
|
c = gsl_complex_mul (c, a);
|
|
|
|
|
for (ii=l;ii>=1;ii--) {
|
|
|
|
|
b = em[ii];
|
|
|
|
|
a = gsl_complex_mul (a, c);
|
|
|
|
|
c = gsl_complex_mul (c, dn);
|
|
|
|
|
dn = gsl_complex_div (gsl_complex_add (en[ii], a), gsl_complex_add (b, a));
|
|
|
|
|
a = gsl_complex_div (c, b);
|
|
|
|
|
}
|
|
|
|
|
a = gsl_complex_div (ONE, gsl_complex_sqrt (gsl_complex_add (ONE, gsl_complex_mul (c, c))));
|
|
|
|
|
if (sn.re >= 0.0) /* gsl_complex_arg (sn) >= 0.0) */
|
|
|
|
|
sn = a;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sn.re = -a.re;
|
|
|
|
|
sn.im = a.im;
|
|
|
|
|
}
|
|
|
|
|
cn = gsl_complex_mul (c, sn);
|
|
|
|
|
}
|
|
|
|
|
if (bo) {
|
|
|
|
|
a=dn;
|
|
|
|
|
dn=cn;
|
|
|
|
|
cn=a;
|
|
|
|
|
sn = gsl_complex_div (sn, d);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cn=gsl_complex_div (ONE, gsl_complex_cosh (u));
|
|
|
|
|
dn=cn;
|
|
|
|
|
sn=gsl_complex_tanh (u);
|
|
|
|
|
}
|
|
|
|
|
if (sn_p)
|
|
|
|
|
*sn_p = sn;
|
|
|
|
|
if (cn_p)
|
|
|
|
|
*cn_p = cn;
|
|
|
|
|
if (dn_p)
|
|
|
|
|
*dn_p = dn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define RF_ERRTOL 0.0025 /* TIMJ, was(float): 0.08 */
|
|
|
|
|
#define RF_TINY 2.2e-307 /* TIMJ, was(float): 1.5e-38 */
|
|
|
|
|
#define RF_BIG 1.5e+307 /* TIMJ, was(float): 3.0e37 */
|
|
|
|
|
#define RF_THIRD (1.0/3.0)
|
|
|
|
|
#define RF_C1 (1.0/24.0)
|
|
|
|
|
#define RF_C2 0.1
|
|
|
|
|
#define RF_C3 (3.0/44.0)
|
|
|
|
|
#define RF_C4 (1.0/14.0)
|
|
|
|
|
|
|
|
|
|
static double
|
|
|
|
|
rf (double x, double y, double z)
|
|
|
|
|
/* Computes Carlson's elliptic integral of the first kind, RF (x, y, z). x, y, and z must be nonneg-
|
|
|
|
|
ative, and at most one can be zero. RF_TINY must be at least 5 times the machine underflow limit,
|
|
|
|
|
RF_BIG at most one fifth the machine overflow limit. */
|
|
|
|
|
{
|
|
|
|
|
double alamb,ave,delx,dely,delz,e2,e3,sqrtx,sqrty,sqrtz,xt,yt,zt;
|
|
|
|
|
|
|
|
|
|
if (1 /* TIMJ: add verbose checks */)
|
|
|
|
|
{
|
|
|
|
|
if (DMIN (DMIN (x, y), z) < 0.0)
|
|
|
|
|
nrerror ("rf: x,y,z have to be positive");
|
|
|
|
|
if (DMIN (DMIN (x + y, x + z), y + z) < RF_TINY)
|
|
|
|
|
nrerror ("rf: only one of x,y,z may be 0");
|
|
|
|
|
if (DMAX (DMAX (x, y), z) > RF_BIG)
|
|
|
|
|
nrerror ("rf: at least one of x,y,z is too big");
|
|
|
|
|
}
|
|
|
|
|
if (DMIN(DMIN(x,y),z) < 0.0 || DMIN(DMIN(x+y,x+z),y+z) < RF_TINY ||
|
|
|
|
|
DMAX(DMAX(x,y),z) > RF_BIG)
|
|
|
|
|
nrerror("invalid arguments in rf");
|
|
|
|
|
xt=x;
|
|
|
|
|
yt=y;
|
|
|
|
|
zt=z;
|
|
|
|
|
do {
|
|
|
|
|
sqrtx=sqrt(xt);
|
|
|
|
|
sqrty=sqrt(yt);
|
|
|
|
|
sqrtz=sqrt(zt);
|
|
|
|
|
alamb=sqrtx*(sqrty+sqrtz)+sqrty*sqrtz;
|
|
|
|
|
xt=0.25*(xt+alamb);
|
|
|
|
|
yt=0.25*(yt+alamb);
|
|
|
|
|
zt=0.25*(zt+alamb);
|
|
|
|
|
ave=RF_THIRD*(xt+yt+zt);
|
|
|
|
|
delx=(ave-xt)/ave;
|
|
|
|
|
dely=(ave-yt)/ave;
|
|
|
|
|
delz=(ave-zt)/ave;
|
|
|
|
|
} while (DMAX(DMAX(fabs(delx),fabs(dely)),fabs(delz)) > RF_ERRTOL);
|
|
|
|
|
e2=delx*dely-delz*delz;
|
|
|
|
|
e3=delx*dely*delz;
|
|
|
|
|
return (1.0+(RF_C1*e2-RF_C2-RF_C3*e3)*e2+RF_C4*e3)/sqrt(ave);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GslComplex
|
|
|
|
|
rfC (GslComplex x, GslComplex y, GslComplex z)
|
|
|
|
|
{
|
|
|
|
|
GslComplex alamb,ave,delx,dely,delz,e2,e3,sqrtx,sqrty,sqrtz,xt,yt,zt;
|
|
|
|
|
GslComplex RFC_C1 = {1.0/24.0, 0}, RFC_C2 = {0.1, 0}, RFC_C3 = {3.0/44.0, 0}, RFC_C4 = {1.0/14.0, 0};
|
|
|
|
|
|
|
|
|
|
if (DMIN (DMIN (gsl_complex_abs (x), gsl_complex_abs (y)), gsl_complex_abs (z)) < 0.0)
|
|
|
|
|
nrerror ("rf: x,y,z have to be positive");
|
|
|
|
|
if (DMIN (DMIN (gsl_complex_abs (x) + gsl_complex_abs (y), gsl_complex_abs (x) + gsl_complex_abs (z)),
|
|
|
|
|
gsl_complex_abs (y) + gsl_complex_abs (z)) < RF_TINY)
|
|
|
|
|
nrerror ("rf: only one of x,y,z may be 0");
|
|
|
|
|
if (DMAX (DMAX (gsl_complex_abs (x), gsl_complex_abs (y)), gsl_complex_abs (z)) > RF_BIG)
|
|
|
|
|
nrerror ("rf: at least one of x,y,z is too big");
|
|
|
|
|
xt=x;
|
|
|
|
|
yt=y;
|
|
|
|
|
zt=z;
|
|
|
|
|
do {
|
|
|
|
|
sqrtx = gsl_complex_sqrt (xt);
|
|
|
|
|
sqrty = gsl_complex_sqrt (yt);
|
|
|
|
|
sqrtz = gsl_complex_sqrt (zt);
|
|
|
|
|
alamb = gsl_complex_add (gsl_complex_mul (sqrtx, gsl_complex_add (sqrty, sqrtz)), gsl_complex_mul (sqrty, sqrtz));
|
|
|
|
|
xt = gsl_complex_mul (gsl_complex (0.25, 0), gsl_complex_add (xt, alamb));
|
|
|
|
|
yt = gsl_complex_mul (gsl_complex (0.25, 0), gsl_complex_add (yt, alamb));
|
|
|
|
|
zt = gsl_complex_mul (gsl_complex (0.25, 0), gsl_complex_add (zt, alamb));
|
|
|
|
|
ave = gsl_complex_mul (gsl_complex (RF_THIRD, 0), gsl_complex_add3 (xt, yt, zt));
|
|
|
|
|
delx = gsl_complex_div (gsl_complex_sub (ave, xt), ave);
|
|
|
|
|
dely = gsl_complex_div (gsl_complex_sub (ave, yt), ave);
|
|
|
|
|
delz = gsl_complex_div (gsl_complex_sub (ave, zt), ave);
|
|
|
|
|
/* } while (DMAX (DMAX (fabs (delx.re), fabs (dely.re)), fabs (delz.re)) > RF_ERRTOL); */
|
|
|
|
|
} while (DMAX (DMAX (gsl_complex_abs (delx), gsl_complex_abs (dely)), gsl_complex_abs (delz)) > RF_ERRTOL);
|
|
|
|
|
e2 = gsl_complex_sub (gsl_complex_mul (delx, dely), gsl_complex_mul (delz, delz));
|
|
|
|
|
e3 = gsl_complex_mul3 (delx, dely, delz);
|
|
|
|
|
return gsl_complex_div (gsl_complex_add3 (gsl_complex (1.0, 0),
|
|
|
|
|
gsl_complex_mul (e2,
|
|
|
|
|
gsl_complex_sub3 (gsl_complex_mul (RFC_C1, e2),
|
|
|
|
|
RFC_C2,
|
|
|
|
|
gsl_complex_mul (RFC_C3, e3))),
|
|
|
|
|
gsl_complex_mul (RFC_C4, e3)),
|
|
|
|
|
gsl_complex_sqrt (ave));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static double
|
|
|
|
|
ellf (double phi, double ak)
|
|
|
|
|
/* Legendre elliptic integral of the 1st kind F(phi, k), evaluated using Carlson's function RF.
|
|
|
|
|
The argument ranges are 0 <= phi <= pi/2, 0 <= k*sin(phi) <= 1. */
|
|
|
|
|
{
|
|
|
|
|
double s=sin(phi);
|
|
|
|
|
return s*rf(DSQR(cos(phi)),(1.0-s*ak)*(1.0+s*ak),1.0);
|
|
|
|
|
}
|