/* * Implements special functions for stable distribution calculations. * * A function g appears in the integrand in Nolan's method for calculating * stable densitities and distribution functions. It takes a different form for * for alpha = 1 vs alpha ≠ 1. See [NO] for more info. * * References * [NO] John P. Nolan (1997) Numerical calculation of stable densities and * distribution functions. */ #define _USE_MATH_DEFINES #include #include #include "levyst.h" /* M_PI et al. are not defined in math.h in C99, even with _USE_MATH_DEFINES */ #ifndef M_PI_2 # define M_PI_2 1.57079632679489661923 /* pi/2 */ # define M_1_PI 0.31830988618379067154 /* 1/pi */ # define M_2_PI 0.63661977236758134308 /* 2/pi */ #endif double g_alpha_ne_one(struct nolan_precanned *sp, double theta) { if (theta == -sp->xi) { if (sp->alpha < 1) { return 0; } else { return INFINITY; } } if (theta == M_PI_2) { if (sp->alpha < 1) { return INFINITY; } else { return 0; } } double cos_theta = cos(theta); return ( sp->zeta_prefactor * pow( cos_theta / sin(sp->alpha_xi + sp->alpha * theta) * sp->zeta_offset, sp->alpha_exp) * cos(sp->alpha_xi + (sp->alpha - 1) * theta) / cos_theta ); } double g_alpha_eq_one(struct nolan_precanned *sp, double theta) { if (theta == -sp->xi) { return 0; } if (theta == M_PI_2) { return INFINITY; } return ( (1 + theta * sp->two_beta_div_pi) * exp((sp->pi_div_two_beta + theta) * tan(theta) - sp->x0_div_term) / cos(theta) ); } struct nolan_precanned * nolan_precan(double alpha, double beta, double x0) { /* Stores results of intermediate computations so they need not be * recomputed when g is called many times during numerical integration * through QUADPACK. */ struct nolan_precanned *sp = malloc(sizeof(struct nolan_precanned)); if (!sp) { abort(); } sp->alpha = alpha; sp->zeta = -beta * tan(M_PI_2 * alpha); if (alpha != 1.) { sp->xi = atan(-sp->zeta) / alpha; sp->zeta_prefactor = pow( pow(sp->zeta, 2.) + 1., -1. / (2. * (alpha - 1.))); sp->alpha_exp = alpha / (alpha - 1.); sp->alpha_xi = atan(-sp->zeta); sp->zeta_offset = x0 - sp->zeta; if (alpha < 1.) { sp->c1 = 0.5 - sp->xi * M_1_PI; sp->c3 = M_1_PI; } else { sp->c1 = 1.; sp->c3 = -M_1_PI; } sp->c2 = alpha * M_1_PI / fabs(alpha - 1.) / (x0 - sp->zeta); sp->g = &g_alpha_ne_one; } else { sp->xi = M_PI_2; sp->two_beta_div_pi = beta * M_2_PI; sp->pi_div_two_beta = M_PI_2 / beta; sp->x0_div_term = x0 / sp->two_beta_div_pi; sp->c1 = 0.; sp->c2 = .5 / fabs(beta); sp->c3 = M_1_PI; sp->g = &g_alpha_eq_one; } return sp; }