[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/gnuplot/gnuplot/master/src/complexfun.c [Back]  [Original]

/* GNUPLOT - complexfun.c */

/*
 * FILE CONTENTS
 *
 * -	BSD 2-clause license covering material in this file
 *
 * -	f_Sign
 *		Sign(z) = z/|z| for complex z
 *
 * -	f_LambertW lambert_initial LambertW
 *		Lambert W function for complex numbers
 *
 * -	f_lnGamma lnGamma
 *		log Gamma for complex argument z
 *	- 14 term Lanczos approximation
 *
 * -	f_Igamma Igamma Igamma_GL Igamma_negative_z Igamma_Poincare
 *		lower incomplete gamma function P(a, z)
 * 	- adapted from previous gnuplot real-valued function igamma(a,x)
 *	- REFERENCE ALGORITHM AS239  APPL. STATIST. (1988) VOL. 37, NO. 3
 *	  B. L. Shea "Chi-Squared and Incomplete Gamma Integral"
 *	- Poincar expansion for large z (not currently used) based on
 *	  Gil et al (2016) ACM TOMS 43:3 Article 26
 *	- Coefficients for Gauss-Legendre quadrature used for a > 100
 *	  Press et al, Numerical Recipes (3rd Ed.) Section 6.2
 *
 * -	Riemann_zeta f_zeta
 *		Riemann zeta function zeta(s) for general complex argument
 *	polynomial series using	algorithm 3 from Borwein [2000] MR1777614
 */

/*[
 * Copyright Ethan A Merritt 2019-2021
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.  Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
]*/

#include "syscfg.h"
#include "gp_types.h"
#include "eval.h"
#include "stdfn.h"
#include "util.h"	/* for int_error() */

#ifdef HAVE_COMPLEX_FUNCS

#include 	/* C99 _Complex */
#include "complexfun.h"

#ifdef HAVE_FENV_H
#include 
#endif

/*
 * Various complex functions like cexp may set errno on underflow
 * We would prefer to return 0.0 rather than NaN
 */
#ifdef HAVE_FENV_H
#define initialize_underflow( who ) \
    if (errno) \
	int_error(NO_CARET, "%s: error present on entry (errno %d %s)", who, errno, strerror(errno)); \
    else feclearexcept(FE_ALL_EXCEPT);
#else
#define initialize_underflow( who ) \
    if (errno) \
	int_error(NO_CARET, "%s: error present on entry (errno %d %s)", who, errno, strerror(errno));
#endif

#ifdef HAVE_FENV_H
#define handle_underflow( who, var ) \
    if (errno) { \
	if (fetestexcept(FE_UNDERFLOW)) { \
	    var = 0.0; \
	    errno = 0; \
	} else { \
	    fprintf(stderr,"%s: errno = %d\n", who, errno); \
	} \
    }
#else
#define handle_underflow( who, var ) int_error(NO_CARET, "%s: errno = %d", who, errno);
#endif

/* internal prototypes */
static complex double lnGamma( complex double z );
static complex double Igamma( complex double a, complex double z );
static double complex Igamma_GL( double complex a, double complex z );
static double complex Igamma_negative_z( double a, double complex z );

#undef IGAMMA_POINCARE
#ifdef IGAMMA_POINCARE
static double complex Igamma_Poincare( double a, double complex z );
#endif

/* wrapper for Igamma so that when it replaces igamma
 * there is still something for old callers who want to call
 * it with real arguments rather than complex.
 */
double
igamma( double a, double z )
{
    return creal( Igamma( (complex double)a, (complex double)z ) );
}

/*
 * Complex Sign function
 * Sign(z) = z/|z| for z non-zero
 */
void
f_Sign(union argument *arg)
{
    struct value result;
    struct value a;
    complex double z;

    pop(&a);	/* Complex argument z */
    if (a.type == INTGR) {
	push(Gcomplex(&result, sgn(a.v.int_val), 0.0));
    } else if (a.type == CMPLX) {
	z = a.v.cmplx_val.real + I*a.v.cmplx_val.imag;
	if (z != 0.0)
	    z = z/cabs(z);
	push(Gcomplex(&result, creal(z), cimag(z)));
    } else
	int_error(NO_CARET, "z must be numeric");
}


/*
 * Lambert W function for complex numbers
 *
 * W(z) is a multi-valued function with the defining property
 * 
 *     z = W(z) exp(W(z))   for complex z
 *
 * LambertW( z, k ) is the kth branch of W
 *
 * This implementation guided by C++ code by Istvn Mez 
 * See also
 *   R. M. Corless, G. H. Gonnet, D. E. G. Hare, D. J. Jeffrey, and D. E. Knuth,
 *   On the Lambert W function, Adv. Comput. Math. 5 (1996), no. 4, 329359.
 *   DOI 10.1007/BF02124750.
 */

/* Internal Prototypes */
complex double lambert_initial(complex double z, int k);
complex double LambertW(complex double z, int k);

void
f_LambertW(union argument *arg)
{
    struct value result;
    struct value a;

    struct cmplx z;	/* gnuplot complex parameter z */
    int k;		/* gnuplot integer parameter k */

    complex double w;	/* C99 _Complex representation */

    pop(&a);		/* Integer argument k */
    if (a.type != INTGR)
	int_error(NO_CARET, "k must be integer");
    k = a.v.int_val;
    pop(&a);		/* Complex argument z */
    if (a.type != CMPLX)
	int_error(NO_CARET, "z must be real or complex");
    z = a.v.cmplx_val;

    w = z.real + I*z.imag;
    w = LambertW( w, k );

    push(Gcomplex(&result, creal(w), cimag(w)));
}

/*
 * First and second derivatives for z * e^z
 * dzexpz( z )  = first derivative of ze^z = e^z + ze^z
 * ddzexpz( z ) = second derivative of ze^z = e^z + e^z + ze^z
 */
#define dzexpz(z)  (cexp(z) + z * cexp(z))
#define ddzexpz(z) (2. * cexp(z) + z * cexp(z))

/*
 * The hard part is choosing a starting point
 * since Halley's method does not have a large radius of convergence
 * EAM: The domain windows in which special case starting points are used
 *      as found in the Mez code produced glitches in my tests.
 *      I adjusted them empirically but I have no justification for
 *      the specific window sizes or thresholds.
 */
complex double
lambert_initial( complex double z, int k )
{
    complex double e = 2.71828182845904523536;
    complex double branch = 2 * M_PI * I * k;
    complex double ip;
    double close;

    double case1_window = 1.2;	/* see note above, was 1.0 */
    double case2_window = 0.9;	/* see note above, was 1.0 */
    double case3_window = 0.5;	/* see note above, was 0.5 */

    /* Initial term of Eq (4.20) from Corless et al */
    ip = clog(z) + branch - clog(clog(z) + branch);

    /* Close to a branch point use (4.22) from Corless et al */
    close = cabs(z - (-1/e));
    if (close  0 || close < case2_window)
		ip = -1. + p - (1./3.) * p*p + (11./72.) * p*p*p;
	}
#if (0)
	/* This treatment empirically causes more glitches than it removes */
	if (k == 1 && cimag(z) < 0.0) {
	    if (creal(z) > 0 && close < case2_window) {
		ip = -1. - p - (1./3.) * p*p - (11./72.) * p*p*p;
		if (cimag(z) > -0.1)
		    ip += (-43./540.) * p*p*p*p;
	    }
	}
#endif
	if (k == -1 && cimag(z) > 0.) {
	    if (close < case2_window)
		ip = -1. - p - (1./3.) * p*p - (11./72.) * p*p*p;
	}
    }

    /* Pad approximant for W(0,a) */
    if (k == 0 && cabs(z - 0.5) 

Web Proxy Viewer  |  New URL  |  Original Page