673 lines
22 KiB
C
673 lines
22 KiB
C
/* vim:set ts=4 sw=2 sts=2 et: */
|
|
|
|
/* This file was imported from a private scientific library
|
|
* based on GSL coined Home Scientific Libray (HSL) by its author
|
|
* Jerome Benoit; this very material is itself inspired from the
|
|
* material written by G. Jungan and distributed by GSL.
|
|
* Ultimately, some modifications were done in order to render the
|
|
* imported material independent from the rest of GSL.
|
|
*/
|
|
|
|
/* `hsl/specfunc/hzeta.c' C source file
|
|
// HSL - Home Scientific Library
|
|
// Copyright (C) 2017-2022 Jerome Benoit
|
|
//
|
|
// HSL 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, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
/*
|
|
// The material in this file is mainly inspired by the material written by
|
|
// G. Jungan and distributed under GPLv2 by the GNU Scientific Library (GSL)
|
|
// ( https://www.gnu.org/software/gsl/ [specfunc/zeta.c]), itself inspired by
|
|
// the material written by Moshier and distributed in the Cephes Mathematical
|
|
// Library ( http://www.moshier.net/ [zeta.c]).
|
|
//
|
|
// More specifically, hsl_sf_hzeta_e is a slightly modifed clone of
|
|
// gsl_sf_hzeta_e as found in GSL 2.4; the remaining is `inspired by'.
|
|
// [Sooner or later a _Working_Note_ may be deposited at ResearchGate
|
|
// ( https://www.researchgate.net/profile/Jerome_Benoit )]
|
|
*/
|
|
|
|
/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */
|
|
|
|
#ifdef _MSC_VER
|
|
#define _USE_MATH_DEFINES
|
|
#endif
|
|
|
|
/* Work around bug in some Windows SDK / MSVC versions where NAN is not a
|
|
* constant expression, triggering an error in the definition of
|
|
* hsl_sf_hzeta_eulermaclaurin_series_coeffs[] and
|
|
* hsl_sf_hzeta_eulermaclaurin_series_majorantratios[] below.
|
|
* We re-define NAN to the value it had in earlier MSVC versions.
|
|
* See https://github.com/igraph/igraph/issues/2701
|
|
* and https://developercommunity.visualstudio.com/t/NAN-is-no-longer-compile-time-constant-i/10688907
|
|
*/
|
|
#ifdef _MSC_VER
|
|
#define _UCRT_NOISY_NAN
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include "hzeta.h"
|
|
#include "plfit_error.h"
|
|
|
|
/* imported from gsl_machine.h */
|
|
|
|
#define GSL_LOG_DBL_MIN (-7.0839641853226408e+02)
|
|
#define GSL_LOG_DBL_MAX 7.0978271289338397e+02
|
|
#define GSL_DBL_EPSILON 2.2204460492503131e-16
|
|
|
|
/* Math constants are not part of standard C.
|
|
* The following are borrowed from igraph/src/core/math.h */
|
|
|
|
#ifndef M_LOG2E
|
|
#define M_LOG2E 1.44269504088896340735992468100189214
|
|
#endif
|
|
|
|
#ifndef M_LN2
|
|
#define M_LN2 0.693147180559945309417232121458176568
|
|
#endif
|
|
|
|
/* imported from gsl_sf_result.h */
|
|
|
|
struct gsl_sf_result_struct {
|
|
double val;
|
|
double err;
|
|
};
|
|
typedef struct gsl_sf_result_struct gsl_sf_result;
|
|
|
|
/* imported and adapted from hsl/specfunc/specfunc_def.h */
|
|
|
|
#define HSL_SF_EVAL_RESULT(FnE) \
|
|
gsl_sf_result result; \
|
|
FnE ; \
|
|
return (result.val);
|
|
|
|
#define HSL_SF_EVAL_TUPLE_RESULT(FnET) \
|
|
gsl_sf_result result0; \
|
|
gsl_sf_result result1; \
|
|
FnET ; \
|
|
*tuple1=result1.val; \
|
|
*tuple0=result0.val; \
|
|
return (result0.val);
|
|
|
|
/* */
|
|
|
|
|
|
#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT 10
|
|
#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER 32
|
|
|
|
#define HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX 256
|
|
|
|
// B_{2j}/(2j)
|
|
static
|
|
double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+2]={
|
|
+1.0,
|
|
+1.0/12.0,
|
|
-1.0/720.0,
|
|
+1.0/30240.0,
|
|
-1.0/1209600.0,
|
|
+1.0/47900160.0,
|
|
-691.0/1307674368000.0,
|
|
+1.0/74724249600.0,
|
|
-3.38968029632258286683019539125e-13,
|
|
+8.58606205627784456413590545043e-15,
|
|
-2.17486869855806187304151642387e-16,
|
|
+5.50900282836022951520265260890e-18,
|
|
-1.39544646858125233407076862641e-19,
|
|
+3.53470703962946747169322997780e-21,
|
|
-8.95351742703754685040261131811e-23,
|
|
+2.26795245233768306031095073887e-24,
|
|
-5.74479066887220244526388198761e-26,
|
|
+1.45517247561486490186626486727e-27,
|
|
-3.68599494066531017818178247991e-29,
|
|
+9.33673425709504467203255515279e-31,
|
|
-2.36502241570062993455963519637e-32,
|
|
+5.99067176248213430465991239682e-34,
|
|
-1.51745488446829026171081313586e-35,
|
|
+3.84375812545418823222944529099e-37,
|
|
-9.73635307264669103526762127925e-39,
|
|
+2.46624704420068095710640028029e-40,
|
|
-6.24707674182074369314875679472e-42,
|
|
+1.58240302446449142975108170683e-43,
|
|
-4.00827368594893596853001219052e-45,
|
|
+1.01530758555695563116307139454e-46,
|
|
-2.57180415824187174992481940976e-48,
|
|
+6.51445603523381493155843485864e-50,
|
|
-1.65013099068965245550609878048e-51,
|
|
NAN}; // hsl_sf_hzeta_eulermaclaurin_series_coeffs
|
|
|
|
// 4\zeta(2j)/(2\pi)^(2j)
|
|
static
|
|
double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+2]={
|
|
-2.0,
|
|
+1.0/6.0,
|
|
+1.0/360.0,
|
|
+1.0/15120.0,
|
|
+1.0/604800.0,
|
|
+1.0/23950080.0,
|
|
+691.0/653837184000.0,
|
|
+1.0/37362124800.0,
|
|
+3617.0/5335311421440000.0,
|
|
+1.71721241125556891282718109009e-14,
|
|
+4.34973739711612374608303284773e-16,
|
|
+1.10180056567204590304053052178e-17,
|
|
+2.79089293716250466814153725281e-19,
|
|
+7.06941407925893494338645995561e-21,
|
|
+1.79070348540750937008052226362e-22,
|
|
+4.53590490467536612062190147774e-24,
|
|
+1.14895813377444048905277639752e-25,
|
|
+2.91034495122972980373252973454e-27,
|
|
+7.37198988133062035636356495982e-29,
|
|
+1.86734685141900893440651103056e-30,
|
|
+4.73004483140125986911927039274e-32,
|
|
+1.19813435249642686093198247936e-33,
|
|
+3.03490976893658052342162627173e-35,
|
|
+7.68751625090837646445889058198e-37,
|
|
+1.94727061452933820705352425585e-38,
|
|
+4.93249408840136191421280056051e-40,
|
|
+1.24941534836414873862975135893e-41,
|
|
+3.16480604892898285950216341362e-43,
|
|
+8.01654737189787193706002438098e-45,
|
|
+2.03061517111391126232614278906e-46,
|
|
+5.14360831648374349984963881946e-48,
|
|
+1.30289120704676298631168697172e-49,
|
|
+3.30026198137930491101219756091e-51,
|
|
NAN}; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios
|
|
|
|
|
|
extern
|
|
int hsl_sf_hzeta_e(const double s, const double q, gsl_sf_result * result) {
|
|
|
|
/* CHECK_POINTER(result) */
|
|
|
|
if ((s <= 1.0) || (q <= 0.0)) {
|
|
PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
|
|
}
|
|
else {
|
|
const double max_bits=54.0; // max_bits=\lceil{s}\rceil with \zeta(s,2)=\zeta(s)-1=GSL_DBL_EPSILON
|
|
const double ln_term0=-s*log(q);
|
|
if (ln_term0 < GSL_LOG_DBL_MIN+1.0) {
|
|
PLFIT_ERROR("underflow", PLFIT_UNDRFLOW);
|
|
}
|
|
else if (GSL_LOG_DBL_MAX-1.0 < ln_term0) {
|
|
PLFIT_ERROR("overflow", PLFIT_OVERFLOW);
|
|
}
|
|
#if 1
|
|
else if (((max_bits < s) && (q < 1.0)) || ((0.5*max_bits < s) && (q < 0.25))) {
|
|
result->val=pow(q,-s);
|
|
result->err=2.0*GSL_DBL_EPSILON*fabs(result->val);
|
|
return (PLFIT_SUCCESS);
|
|
}
|
|
else if ((0.5*max_bits < s) && (q < 1.0)) {
|
|
const double a0=pow(q,-s);
|
|
const double p1=pow(q/(1.0+q),s);
|
|
const double p2=pow(q/(2.0+q),s);
|
|
const double ans=a0*(1.0+p1+p2);
|
|
result->val=ans;
|
|
result->err=GSL_DBL_EPSILON*(2.0+0.5*s)*fabs(result->val);
|
|
return (PLFIT_SUCCESS);
|
|
}
|
|
#endif
|
|
else { // Euler-Maclaurin summation formula
|
|
const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q;
|
|
const double inv_qshift=1.0/qshift;
|
|
const double sqr_inv_qshift=inv_qshift*inv_qshift;
|
|
const double inv_sm1=1.0/(s-1.0);
|
|
const double pmax=pow(qshift,-s);
|
|
double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
|
|
double delta=NAN;
|
|
double tscp=s;
|
|
double scp=tscp;
|
|
double pcp=pmax*inv_qshift;
|
|
double ratio=scp*pcp;
|
|
size_t n=0;
|
|
size_t j=0;
|
|
double ans=0.0;
|
|
double mjr=NAN;
|
|
|
|
for(j=0;j<HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT;++j) ans+=(terms[n++]=pow(j+q,-s));
|
|
ans+=(terms[n++]=0.5*pmax);
|
|
ans+=(terms[n++]=pmax*qshift*inv_sm1);
|
|
for(j=1;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
|
|
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
|
|
ans+=(terms[n++]=delta);
|
|
scp*=++tscp;
|
|
scp*=++tscp;
|
|
pcp*=sqr_inv_qshift;
|
|
ratio=scp*pcp;
|
|
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
|
|
}
|
|
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
|
|
|
|
ans=0.0; while (n) ans+=terms[--n];
|
|
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
|
|
|
|
result->val=+ans;
|
|
result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr);
|
|
return (PLFIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extern
|
|
double hsl_sf_hzeta(const double s, const double q) {
|
|
HSL_SF_EVAL_RESULT(hsl_sf_hzeta_e(s,q,&result)); }
|
|
|
|
extern
|
|
int hsl_sf_hzeta_deriv_e(const double s, const double q, gsl_sf_result * result) {
|
|
|
|
/* CHECK_POINTER(result) */
|
|
|
|
if ((s <= 1.0) || (q <= 0.0)) {
|
|
PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
|
|
}
|
|
else {
|
|
const double ln_hz_term0=-s*log(q);
|
|
if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) {
|
|
PLFIT_ERROR("underflow", PLFIT_UNDRFLOW);
|
|
}
|
|
else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) {
|
|
PLFIT_ERROR("overflow", PLFIT_OVERFLOW);
|
|
}
|
|
else { // Euler-Maclaurin summation formula
|
|
const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q;
|
|
const double inv_qshift=1.0/qshift;
|
|
const double sqr_inv_qshift=inv_qshift*inv_qshift;
|
|
const double inv_sm1=1.0/(s-1.0);
|
|
const double pmax=pow(qshift,-s);
|
|
const double lmax=log(qshift);
|
|
double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
|
|
double delta=NAN;
|
|
double tscp=s;
|
|
double scp=tscp;
|
|
double pcp=pmax*inv_qshift;
|
|
double lcp=lmax-1.0/s;
|
|
double ratio=scp*pcp*lcp;
|
|
double qs=NAN;
|
|
size_t n=0;
|
|
size_t j=0;
|
|
double ans=0.0;
|
|
double mjr=NAN;
|
|
|
|
for(j=0,qs=q;j<HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT;++qs,++j) ans+=(terms[n++]=log(qs)*pow(qs,-s));
|
|
ans+=(terms[n++]=0.5*lmax*pmax);
|
|
ans+=(terms[n++]=pmax*qshift*inv_sm1*(lmax+inv_sm1));
|
|
for(j=1;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
|
|
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
|
|
ans+=(terms[n++]=delta);
|
|
scp*=++tscp; lcp-=1.0/tscp;
|
|
scp*=++tscp; lcp-=1.0/tscp;
|
|
pcp*=sqr_inv_qshift;
|
|
ratio=scp*pcp*lcp;
|
|
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
|
|
}
|
|
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
|
|
|
|
ans=0.0; while (n) ans+=terms[--n];
|
|
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
|
|
|
|
result->val=-ans;
|
|
result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr);
|
|
return (PLFIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extern
|
|
double hsl_sf_hzeta_deriv(const double s, const double q) {
|
|
HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv_e(s,q,&result)); }
|
|
|
|
extern
|
|
int hsl_sf_hzeta_deriv2_e(const double s, const double q, gsl_sf_result * result) {
|
|
|
|
/* CHECK_POINTER(result) */
|
|
|
|
if ((s <= 1.0) || (q <= 0.0)) {
|
|
PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
|
|
}
|
|
else {
|
|
const double ln_hz_term0=-s*log(q);
|
|
if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) {
|
|
PLFIT_ERROR("underflow", PLFIT_UNDRFLOW);
|
|
}
|
|
else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) {
|
|
PLFIT_ERROR("overflow", PLFIT_OVERFLOW);
|
|
}
|
|
else { // Euler-Maclaurin summation formula
|
|
const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q;
|
|
const double inv_qshift=1.0/qshift;
|
|
const double sqr_inv_qshift=inv_qshift*inv_qshift;
|
|
const double inv_sm1=1.0/(s-1.0);
|
|
const double pmax=pow(qshift,-s);
|
|
const double lmax=log(qshift);
|
|
const double lmax_p_inv_sm1=lmax+inv_sm1;
|
|
const double sqr_inv_sm1=inv_sm1*inv_sm1;
|
|
const double sqr_lmax=lmax*lmax;
|
|
const double sqr_lmax_p_inv_sm1=lmax_p_inv_sm1*lmax_p_inv_sm1;
|
|
double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
|
|
double delta=NAN;
|
|
double tscp=s;
|
|
double slcp=NAN;
|
|
double plcp=NAN;
|
|
double scp=tscp;
|
|
double pcp=pmax*inv_qshift;
|
|
double lcp=1.0/s-lmax;
|
|
double sqr_lcp=lmax*(lmax-2.0/s);
|
|
double ratio=scp*pcp*sqr_lcp;
|
|
double qs=NAN;
|
|
double lqs=NAN;
|
|
size_t n=0;
|
|
size_t j=0;
|
|
double ans=0.0;
|
|
double mjr=NAN;
|
|
|
|
for(j=0,qs=q;j<HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT;++qs,++j) {
|
|
lqs=log(qs);
|
|
ans+=(terms[n++]=lqs*lqs*pow(qs,-s));
|
|
}
|
|
ans+=(terms[n++]=0.5*sqr_lmax*pmax);
|
|
ans+=(terms[n++]=pmax*qshift*inv_sm1*(sqr_lmax_p_inv_sm1+sqr_inv_sm1));
|
|
for(j=1;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
|
|
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
|
|
ans+=(terms[n++]=delta);
|
|
scp*=++tscp; slcp=plcp=1.0/tscp;
|
|
scp*=++tscp; slcp+=1.0/tscp; plcp/=tscp;
|
|
pcp*=sqr_inv_qshift;
|
|
sqr_lcp+=2.0*(plcp+slcp*lcp);
|
|
ratio=scp*pcp*sqr_lcp;
|
|
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
|
|
lcp+=slcp;
|
|
}
|
|
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
|
|
|
|
ans=0.0; while (n) ans+=terms[--n];
|
|
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
|
|
|
|
result->val=+ans;
|
|
result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr);
|
|
return (PLFIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extern
|
|
double hsl_sf_hzeta_deriv2(const double s, const double q) {
|
|
HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv2_e(s,q,&result)); }
|
|
|
|
static inline
|
|
double hsl_sf_hZeta0_zed(const double s, const double q) {
|
|
#if 1
|
|
const long double ld_q=(long double)(q);
|
|
const long double ld_s=(long double)(s);
|
|
const long double ld_log1prq=log1pl(1.0L/ld_q);
|
|
const long double ld_epsilon=expm1l(-ld_s*ld_log1prq);
|
|
const long double ld_z=ld_s+(ld_q+0.5L*ld_s+0.5L)*ld_epsilon;
|
|
const double z=(double)(ld_z);
|
|
#else
|
|
double z=s+(q+0.5*s+0.5)*expm1(-s*log1p(1.0/q));
|
|
#endif
|
|
return (z); }
|
|
|
|
// Z_{0}(s,a) = a^s \left(\frac{1}{2}+\frac{a}{s-1}\right)^{-1} \zeta(s,a) - 1
|
|
// Z_{0}(s,a) = O\left(\frac{(s-1)s}{6a^{2}}\right)
|
|
static
|
|
int hsl_sf_hZeta0(const double s, const double q, double * value, double * abserror) {
|
|
const double criterion=ceil(10.0*s-q);
|
|
const size_t shift=(criterion<0.0)?0:
|
|
(criterion<HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX)?(size_t)(llrint(criterion)):
|
|
HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX;
|
|
const double qshift=(double)(shift)+q;
|
|
const double inv_qshift=1.0/qshift;
|
|
const double sqr_inv_qshift=inv_qshift*inv_qshift;
|
|
const double sm1=s-1.0;
|
|
double terms[HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
|
|
double delta=NAN;
|
|
double tscp=s;
|
|
double scp=s*sm1;
|
|
double pcp=inv_qshift/(2.0*qshift+sm1);
|
|
double ratio=NAN;
|
|
size_t n=0;
|
|
size_t j=0;
|
|
double ans=0.0;
|
|
double mjr=NAN;
|
|
|
|
if (shift) {
|
|
const double hsm1=0.5*sm1;
|
|
const double inv_q=1.0/q;
|
|
const double qphsm1=q+hsm1;
|
|
const double inv_qphsm1=1.0/qphsm1;
|
|
const double qshiftphsm1=qshift+hsm1;
|
|
double qs=q;
|
|
double a=1.0;
|
|
for(j=0;j<shift;) {
|
|
ans+=(terms[n++]=a*hsl_sf_hZeta0_zed(s,qs++)*inv_qphsm1);
|
|
a=exp(-s*log1p((++j)*inv_q));
|
|
}
|
|
pcp*=a*qshiftphsm1*inv_qphsm1;
|
|
}
|
|
ratio=scp*pcp;
|
|
ans+=terms[n++]=ratio/6.0;
|
|
scp*=++tscp;
|
|
scp*=++tscp;
|
|
pcp*=2.0*sqr_inv_qshift;
|
|
ratio=scp*pcp;
|
|
for(j=2;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
|
|
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
|
|
ans+=(terms[n++]=delta);
|
|
scp*=++tscp;
|
|
scp*=++tscp;
|
|
pcp*=sqr_inv_qshift;
|
|
ratio=scp*pcp;
|
|
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
|
|
}
|
|
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
|
|
|
|
ans=0.0; while (n) ans+=terms[--n];
|
|
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
|
|
|
|
*value=ans;
|
|
*abserror=2.0*((shift+1)*GSL_DBL_EPSILON*fabs(ans)+mjr);
|
|
|
|
return (PLFIT_SUCCESS); }
|
|
|
|
static inline
|
|
double hsl_sf_hZeta1_zed(const double s, const double q) {
|
|
#if 1
|
|
const long double ld_q=(long double)(q);
|
|
const long double ld_s=(long double)(s);
|
|
const long double ld_sm1=ld_s-1.0L;
|
|
const long double ld_logq=logl(ld_q);
|
|
const long double ld_log1prq=log1pl(1.0L/ld_q);
|
|
const long double ld_inv_logq=1.0L/ld_logq;
|
|
const long double ld_logratiom1=ld_log1prq*ld_inv_logq;
|
|
const long double ld_powratiom1=expm1l(-ld_s*ld_log1prq);
|
|
const long double ld_varepsilon=expm1l(-ld_sm1*ld_log1prq);
|
|
const long double ld_epsilon=ld_logratiom1+ld_powratiom1+ld_logratiom1*ld_powratiom1;
|
|
const long double ld_z=ld_s+(ld_q+0.5L*ld_s+0.5L)*ld_epsilon+ld_q/ld_sm1*ld_inv_logq*ld_varepsilon;
|
|
const double z=(double)(ld_z);
|
|
#else
|
|
const double sm1=s-1.0;
|
|
const double inv_ln_q=1.0/log(q);
|
|
const double log1prq=log1p(1.0/q);
|
|
const double logratiom1=log1prq*inv_ln_q;
|
|
const double powratiom1=expm1(-s*log1prq);
|
|
const double epsilon=logratiom1+powratiom1+logratiom1*powratiom1;
|
|
const double z=s+(q+0.5*s+0.5)*epsilon+q/sm1*inv_ln_q*expm1(-sm1*log1prq);
|
|
#endif
|
|
return (z); }
|
|
|
|
// Z_{1}(s,a) = -\frac{a^s}{\ln(a)} \left(\frac{1}{2}+\frac{a}{s-1}\,\left[1+\frac{1}{(s-1)\,\ln(a)}\right]\right)^{-1} \zeta^{\prime}(s,a) - 1
|
|
// Z_{1}(s,a) = O\left(\frac{(s-1)s}{6a^{2}}\right)
|
|
static
|
|
int hsl_sf_hZeta1(const double s, const double q, const double ln_q, double * value, double * abserror, double * coeff1) {
|
|
const double criterion=ceil(10.0*s-q);
|
|
const size_t shift=(criterion<0.0)?0:
|
|
(criterion<HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX)?(size_t)(llrint(criterion)):
|
|
HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX;
|
|
const double qshift=(double)(shift)+q;
|
|
const double ln_qshift=log(qshift);
|
|
const double inv_qshift=1.0/qshift;
|
|
const double inv_ln_q=1.0/ln_q;
|
|
const double inv_ln_qshift=1.0/ln_qshift;
|
|
const double sqr_inv_qshift=inv_qshift*inv_qshift;
|
|
const double sm1=s-1.0;
|
|
const double hsm1=0.5*sm1;
|
|
const double q_over_ln_q=q*inv_ln_q;
|
|
const double qshift_over_ln_qshift=qshift*inv_ln_qshift;
|
|
const double qphsm1=q+hsm1;
|
|
const double qshiftphsm1=qshift+hsm1;
|
|
double terms[HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
|
|
double delta=NAN;
|
|
double tscp=s;
|
|
double scp=s*sm1;
|
|
double pcp=inv_qshift*sm1/(qshift_over_ln_qshift+sm1*qshiftphsm1);
|
|
double lcp=1.0-inv_ln_qshift/s;
|
|
double ratio=NAN;
|
|
size_t n=0;
|
|
size_t j=0;
|
|
double ans=0.0;
|
|
double mjr=NAN;
|
|
|
|
if (shift) {
|
|
const double inv_q=1.0/q;
|
|
const double inv_sm1=1.0/sm1;
|
|
const double w=1.0+inv_sm1*inv_ln_q;
|
|
const double wshift=1.0+inv_sm1*inv_ln_qshift;
|
|
const double qwphsm1=q*w+hsm1;
|
|
const double inv_qwphsm1=1.0/qwphsm1;
|
|
const double qshiftwshiftphsm1=qshift*wshift+hsm1;
|
|
double qs=q;
|
|
double a=1.0;
|
|
for(j=0;j<shift;) {
|
|
ans+=(terms[n++]=a*hsl_sf_hZeta1_zed(s,qs++)*inv_qwphsm1);
|
|
a=log(qs)*inv_ln_q*exp(-s*log1p((++j)*inv_q));
|
|
}
|
|
pcp*=a*qshiftwshiftphsm1*inv_qwphsm1;
|
|
}
|
|
ratio=scp*pcp*lcp;
|
|
ans+=terms[n++]=ratio/12.0;
|
|
scp*=++tscp; lcp-=inv_ln_qshift/tscp;
|
|
scp*=++tscp; lcp-=inv_ln_qshift/tscp;
|
|
pcp*=sqr_inv_qshift;
|
|
ratio=scp*pcp*lcp;
|
|
for(j=2;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
|
|
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
|
|
ans+=(terms[n++]=delta);
|
|
scp*=++tscp; lcp-=inv_ln_qshift/tscp;
|
|
scp*=++tscp; lcp-=inv_ln_qshift/tscp;
|
|
pcp*=sqr_inv_qshift;
|
|
ratio=scp*pcp*lcp;
|
|
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
|
|
}
|
|
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
|
|
|
|
ans=0.0; while (n) ans+=terms[--n];
|
|
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
|
|
|
|
*value=ans;
|
|
*abserror=2.0*((shift+1)*GSL_DBL_EPSILON*fabs(ans)+mjr);
|
|
|
|
if (coeff1) *coeff1=1.0+q_over_ln_q/qphsm1/sm1;
|
|
|
|
return (PLFIT_SUCCESS); }
|
|
|
|
extern
|
|
int hsl_sf_lnhzeta_deriv_tuple_e(const double s, const double q, gsl_sf_result * result, gsl_sf_result * result_deriv) {
|
|
|
|
/* CHECK_POINTER(result) */
|
|
|
|
if ((s <= 1.0) || (q <= 0.0)) {
|
|
PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
|
|
}
|
|
else if (q == 1.0) {
|
|
const double inv_sm1=1.0/(s-1.0);
|
|
const double inv_qsm1=4.0*inv_sm1;
|
|
const double hz_coeff0=exp2(s+1.0);
|
|
const double hz_coeff1=1.0+inv_qsm1;
|
|
double hZeta0_value=NAN;
|
|
double hZeta0_abserror=NAN;
|
|
hsl_sf_hZeta0(s,2.0,&hZeta0_value,&hZeta0_abserror);
|
|
hZeta0_value+=1.0;
|
|
if (result) {
|
|
const double ln_hz_coeff=hz_coeff1/hz_coeff0;
|
|
const double ln_hZeta0_value=ln_hz_coeff*hZeta0_value;
|
|
result->val=log1p(ln_hZeta0_value);
|
|
result->err=(2.0*GSL_DBL_EPSILON*ln_hz_coeff+hZeta0_abserror)/(1.0+ln_hZeta0_value);
|
|
}
|
|
|
|
if (result_deriv) {
|
|
const double ld_hz_coeff2=1.0+inv_sm1*M_LOG2E;
|
|
const double ld_hz_coeff1=1.0+inv_qsm1*ld_hz_coeff2;
|
|
double hZeta1_value=NAN;
|
|
double hZeta1_abserror=NAN;
|
|
hsl_sf_hZeta1(s,2.0,M_LN2,&hZeta1_value,&hZeta1_abserror,NULL);
|
|
hZeta0_value*=hz_coeff1;
|
|
hZeta0_value+=hz_coeff0;
|
|
hZeta1_value+=1.0;
|
|
hZeta1_value*=-M_LN2*ld_hz_coeff1;
|
|
result_deriv->val=hZeta1_value/hZeta0_value;
|
|
result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror);
|
|
}
|
|
}
|
|
else {
|
|
const double ln_q=log(q);
|
|
double hZeta0_value=NAN;
|
|
double hZeta0_abserror=NAN;
|
|
hsl_sf_hZeta0(s,q,&hZeta0_value,&hZeta0_abserror);
|
|
if (result) {
|
|
const double ln_hz_term0=-s*ln_q;
|
|
const double ln_hz_term1=log(0.5+q/(s-1.0));
|
|
result->val=ln_hz_term0+ln_hz_term1+log1p(hZeta0_value);
|
|
result->err=2.0*GSL_DBL_EPSILON*(fabs(ln_hz_term0)+fabs(ln_hz_term1))+hZeta0_abserror/(1.0+hZeta0_value);
|
|
}
|
|
if (result_deriv) {
|
|
double hZeta1_value=NAN;
|
|
double hZeta1_abserror=NAN;
|
|
double ld_hz_coeff1=NAN;
|
|
hsl_sf_hZeta1(s,q,ln_q,&hZeta1_value,&hZeta1_abserror,&ld_hz_coeff1);
|
|
result_deriv->val=-ln_q*ld_hz_coeff1*(1.0+hZeta1_value)/(1.0+hZeta0_value);
|
|
result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror);
|
|
}
|
|
}
|
|
|
|
return (PLFIT_SUCCESS); }
|
|
|
|
extern
|
|
double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * tuple0, double * tuple1) {
|
|
HSL_SF_EVAL_TUPLE_RESULT(hsl_sf_lnhzeta_deriv_tuple_e(s,q,&result0,&result1)); }
|
|
|
|
extern
|
|
int hsl_sf_lnhzeta_e(const double s, const double q, gsl_sf_result * result) {
|
|
return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,result,NULL)); }
|
|
|
|
extern
|
|
double hsl_sf_lnhzeta(const double s, const double q) {
|
|
HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_e(s,q,&result)); }
|
|
|
|
extern
|
|
int hsl_sf_lnhzeta_deriv_e(const double s, const double q, gsl_sf_result * result) {
|
|
return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,NULL,result)); }
|
|
|
|
extern
|
|
double hsl_sf_lnhzeta_deriv(const double s, const double q) {
|
|
HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_deriv_e(s,q,&result)); }
|
|
|
|
//
|
|
// End of file `hsl/specfunc/hzeta.c'.
|