Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241697 views
/************* ComputeL v1.3.4, 2001-2016, (c) Tim Dokchitser ************/
/**************** computing special values of L-functions ****************/
/* arXiv.org/abs/math.NT/0207280, Exper. Math. 13 (2004), no. 2, 137-150 */
/****** Questions/comments welcome! -> [email protected] ******/

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ Distributed under the terms of the GNU General Public License (GPL)
\\    This code 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.
\\ The full text of the GPL is available at:
\\                 http://www.gnu.org/licenses/
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

\\ USAGE:   Take an L-function L(s) = sum of a(n)/n^s over complex numbers
\\          e.g. Riemann zeta-function, Dedekind zeta-function,
\\               Dirichlet L-function of a character, L-function
\\               of a curve over a number field, L-function of a modular form,
\\               any ``motivic'' L-function, Shintani's zeta-function etc.
\\          assuming L(s) satisfies a functional equation of a standard form,
\\          this package computes L(s) or its k-th derivative for some k
\\          for a given complex s to required precision
\\          - a short usage guide is provided below
\\          - or (better) just look at the example files ex-*
\\            they are hopefully self-explanatory
\\
\\ ASSUMED: L^*(s) = Gamma-factor * L(s) satisfies functional equation
\\          L^*(s) = sgn * L^*(weight-s),
\\            [ more generally L^*(s) = sgn * Ldual^*(weight-s) ]
\\          Gamma-factor = A^s * product of Gamma((s+gammaV[k])/2)
\\            where A = sqrt(conductor/Pi^d)
\\
\\      gammaV    = list of Gamma-factor parameters,
\\                  e.g. [0] for Riemann zeta, [0,1] for ell.curves
\\      conductor = exponential factor (real>0, usually integer),
\\                  e.g. 1 for Riemann zeta and modular forms under SL_2(Z)
\\                  e.g. |discriminant| for number fields
\\                  e.g. conductor for H^1 of curves/Q
\\      weight    = real > 0      (usually integer, =1 by default)
\\                  e.g. 1 for Riemann zeta, 2 for H^1 of curves/Q
\\      sgn       = complex number   (=1 by default)
\\
\\ 1. Read the package (\rcomputel)
\\ 2. Set the required working precision (say \p28)
\\
\\ 3. DEFINE gammaV, conductor, weight, sgn,
\\           Lpoles = vector of points where L^*(s) has (simple) poles
\\             Only poles with Re(s)>weight/2 are to be included
\\           Lresidues = vector of residues of L^*(s) in those poles
\\             or set Lresidues = automatic (default value; see ex-nf)
\\           if necessary, re-define coefgrow(), MaxImaginaryPart (see below)        
\\
\\ [4.] CALL cflength()   determine how many coefficients a(n) are necessary
\\      [optional]        to perform L-function computations
\\
\\ 5. CALL initLdata(cfstr) where cfstr (e.g. "(-1)^k") is a string which
\\         evaluates to k-th coefficient a(k) in L-series, e.g.
\\      N    = cflength();                      \\ say returns N=10
\\      Avec = [1,-1,0,1,-1,0,1,-1,0,1,-1,0];   \\ must be at least 10 long
\\      initLdata("Avec[k]");
\\    If Ldual(s)<>L(s), in other words, if the functional equation involves
\\    another L-function, its coefficients are passed as a 3rd parameter,
\\      initLdata("Avec[k]",,"conj(Avec[k])"); see ex-chgen as an example
\\
\\ [7.] CALL checkfeq()     verify how well numerically the functional
\\      [optional]          equation is satisfied
\\                          also determines the residues if Lpoles!=[]
\\                          and Lresidues=automatic
\\    More specifically: for T>1 (default 1.2), checkfeq(T) should ideally
\\    return 0 (with current precision, e.g. 3.2341E-29 for \p28 is good)
\\      * if what checkfeq() returns does not look like 0 at all,
\\        probably functional equation is wrong
\\        (i.e. some of the parameters gammaV, conductor etc., or the coeffs)
\\      * if checkfeq(T) is to be used, more coefficients have to be
\\        generated (approximately T times more), e.g. call
\\           cflength(1.3), initLdata("a(k)",1.3), checkfeq(1.3)
\\      * T=1 always (!) returns 0, so T has to be away from 1
\\      * default value T=1.2 seems to give a reasonable balance
\\      * if you don't have to verify the functional equation or the L-values,
\\           call cflength(1) and initLdata("a(k)",1),
\\           you need slightly less coefficients then
\\
\\ 8. CALL L(s0)    to determine the value of L-function L(s) in s=s0
\\    CALL L(s0,c)  with c>1 to get the same value with a different cutoff
\\                  point (c close to 1); should return the same answer,
\\                  good to check if everything works with right precision
\\                  (if it doesn't, email me!)
\\                  needs generally more coefficients for larger ex
\\                  if L(s0,ex)-L(s0) is large, either the functional eq.
\\                  is wrong or loss of precision (should get a warning)
\\    CALL L(s0,,k) to determine k-th derivative of L(s) in s=s0
\\                  see ex-bsw for example
\\    CALL Lseries(s,,k) to get first k terms of Taylor series expansion
\\                        L(s)+L'(s)S+L''(s)*S^2/2!+...
\\                  faster than k calls to L(s)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\      Default values for the L-function parameters                      \\
\\      All may be (and conductor and gammaV must be) re-defined          \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

\\ MUST be re-defined, gives error if unchanged
conductor = automatic;
gammaV    = automatic;

\\ MAY be re-defined
weight    = 1;         \\ by default L(s)<->L(1-s)
sgn       = 1;         \\            with sign=1 in functional equation
Lpoles    = [];        \\            and L*(s) has no poles
Lresidues = automatic; \\ if this is not changed to [r1,r2,...] by hand,
                       \\ checkfeq() tries to determine residues automatically
                       \\ see ex-nf for instance

{
coefgrow(n) = if(length(Lpoles),    \\ default bound for coeffs. a(n)
   1.5*n^(vecmax(real(Lpoles))-1),  \\ you may redefine coefgrow() by hand
   sqrt(4*n)^(weight-1));           \\ if your a(n) have different growth
}                                   \\ see ex-delta for example

\\ - For s with large imaginary part there is a lot of cancellation when
\\ computing L(s), so a precision loss occurs. You then get a warning message
\\ - If you want to compute L(s), say, for s=1/2+100*I,
\\ set MaxImaginaryPart=100 before calling initLdata()
\\ - global variable PrecisionLoss holds the number of digits lost in
\\ the last calculation (indepedently of the MaxImaginaryPart setting)

MaxImaginaryPart = 0;    \\ re-define this if you want to compute L(s)
                         \\ for large imaginary s (see ex-zeta2 for example)

MaxAsympCoeffs  = 40;    \\ At most this number of terms is generated
                         \\ in asymptotic series for phi(t) and G(s,t)
                         \\ default value of 40 seems to work generally well


/******************* IMPLEMENTATION OF THE PACKAGE ************************/


\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\         Some helfpul functions                                         \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

\\ Extraction operations on vectors
vecleft(v,n)  = vecextract(v,concat("1..",Str(n)));
vecright(v,n) = vecextract(v,concat(Str(length(v)-n+1),concat("..",Str(length(v)))));

\\ Tabulate a string to n characters, e.g. StrTab(3,2)="3 ";
StrTab(x,n) = x=Str(x);while(length(x)<n,x=concat(x," "));x

\\ Concatenate up to 4 strings
concatstr(s1="",s2="",s3="",s4="")=concat(Str(s1),concat(Str(s2),concat(Str(s3),Str(s4))))

\\ Print a ``small error'', e.g. 0.00000013 as "1E-7"
{
errprint(x)=if(type(x)=="t_COMPLEX",x=abs(x));
   if(x==0,concatstr("1E-",default(realprecision)+1),
   concatstr(truncate(x/10^floor(log(abs(x))/log(10))),"E",floor(log(abs(x))/log(10))));
}


\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ gammaseries(z0,terms)                                                   \\
\\ Taylor series expansion of Gamma(z0+x) around 0, z0 arbitrary complex   \\
\\ - up to O(x^(terms+1))                                                  \\
\\ - uses current real precision                                           \\
\\ See Luke "Mathematical functions and their approximations", section 1.4  \
\\ note a misprint there in the recursion formulas [(z-n) term in c3 below] \
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
gammaseries(z0,terms)=
  local(Avec,Bvec,Qvec,n,z,err,res,c0,c1,c2,c3,sinser,reflect,digts,srprec,negint);

  srprec=default(seriesprecision);
  if (z0==real(round(z0)),z0=real(round(z0)));    \\ you don't want to know
  negint=type(z0)=="t_INT" && z0<=0;              \\ z0 is a pole
  default(seriesprecision,terms+1+negint);
  if (terms==0 && !negint,res=gamma(z0)+O(x),     \\ faster to use
  if (terms==1 && !imag(z0) && !negint,           \\   built-in functions
      res=gamma(z0)*(1+psi(z0)*x+O(x^2)),         \\   in special cases
  if (z0==0, res=gamma(1+x)/x,
  if (z0==1, res=gamma(1+x),
  if (z0==2, res=gamma(1+x)*(1+x),
  \\ otherwise use Luke's rational approximations for psi(x)
  digts=default(realprecision);      \\ save working precision
  default(realprecision,digts+3);    \\   and work with 3 digits more
  reflect=real(z0)<0.5;              \\ left of 1/2 use reflection formula
  if (reflect,z0=1-z0);
  z=subst(Ser(precision(1.*z0,digts+3)+X),X,x);
    \\ work with z0+x as a variable gives power series in X as an answer
  Avec=[1,(z+6)/2,(z^2+82*z+96)/6,(z^3+387*z^2+2906*z+1920)/12];
  Bvec=[1,4,8*z+28,14*z^2+204*z+310];
  Qvec=[0,0,0,Avec[4]/Bvec[4]];
  n=4;
  until(err<0.1^(digts+1.5),         \\ Luke's recursions for psi(x)
    c1=(2*n-1)*(3*(n-1)*z+7*n^2-9*n-6);
    c2=-(2*n-3)*(z-n-1)*(3*(n-1)*z-7*n^2+19*n-4);
    c3=(2*n-1)*(n-3)*(z-n)*(z-n-1)*(z+n-4);
    c0=(2*n-3)*(n+1);
    Avec=concat(Avec,[(c1*Avec[n]+c2*Avec[n-1]+c3*Avec[n-2])/c0]);
    Bvec=concat(Bvec,[(c1*Bvec[n]+c2*Bvec[n-1]+c3*Bvec[n-2])/c0]);
    Qvec=concat(Qvec,Avec[n+1]/Bvec[n+1]);
    err=vecmax(abs(Vec(Qvec[n+1]-Qvec[n])));
    n++;
  );
  res=gamma(z0)*exp(intformal( psi(1)+2*(z-1)/z*Qvec[n] )); \\ psi->gamma
  if (reflect,                        \\ reflect if necessary
    sinser=Vec(sin(Pi*z));
    if (negint,sinser[1]=0);          \\ taking slight care at integers<0
    res=subst(Pi/res/Ser(sinser),x,-x);
  );
  default(realprecision,digts);
  )))));
  default(seriesprecision,srprec);
  res;
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ fullgamma(ss) - the full gamma factor (at s=ss)                        \\
\\   vA^s*Gamma((s+gammaV[1])/2)*...*Gamma((s+gammaV[d])/2)               \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
fullgamma(ss) = 
  if(ss!=lastFGs,lastFGs=ss;
    lastFGval=prod(j=1,length(gammaV),gamma((ss+gammaV[j])/2),vA^ss)
  );
  lastFGval;
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ fullgammaseries(ss,extraterms) - Laurent series for the gamma factor   \\
\\                                  without the exponential factor, i.e.  \\
\\ Gamma((s+gammaV[1])/2)*...*Gamma((s+gammaV[d])/2)                      \\
\\ around s=ss with a given number of extra terms. The series variable is S.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
fullgammaseries(ss,extraterms)=
  local(digts,GSD);
  
  digts=default(realprecision);
  if (lastFGSs!=ss || lastFGSterms!=extraterms,
    GSD=sum(j=1,numpoles,(abs((ss+poles[j])/2-round(real((ss+poles[j])/2)))<10^(2-digts)) * PoleOrders[j] )+extraterms;
    lastFGSs=ss;
    lastFGSterms=extraterms;
    lastFGSval=subst(prod(j=1,length(gammaV),gammaseries((ss+gammaV[j])/2,GSD)),x,S/2);
  );
 lastFGSval;
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\         RecursionsAtInfinity(gammaV)                                   \\
\\   Recursions for the asymptotic expansion coefficients                 \\
\\   for phi(x) and G(s,x) at infinity.                                   \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
RecursionsAtInfinity(gammaV)=
  local(d,p,j,k,symvec,modsymvec,deltapol,recF,recG);

  \\ d = number of Gamma-factors in question
  \\ gammaV[k] = Gamma-factors
  \\ symvec = vector of elementary symmetric functions
  \\   1, gammaV[1]+...+gammaV[d], ... , gammaV[1]*...*gammaV[d], 0
  \\ modsymvec = symmetric expressions used in the formula

  d      = length(gammaV);
  symvec = concat(Vec(prod(k=1,d,(x+gammaV[k]))),[0]);

  modsymvec = vector(d+2,k,0);
  for (j=0,d,
  for (k=0,j,
    modsymvec[j+1]+=(-symvec[2])^k*d^(j-1-k)*binomial(k+d-j,k)*symvec[j-k+1];
  ));

  \\ Delta polynomials
  OldSeriesPrecision = default(seriesprecision);
  default(seriesprecision,2*d+2);
  deltapol=subst(Vec( (sinh(x)/x)^tvar ),tvar,x);
  default(seriesprecision,OldSeriesPrecision);

  \\ recursion coefficients for phi at infinity
  recF=vector(d+1,p,
    -1/2^p/d^(p-1)/n*sum(m=0,p,modsymvec[m+1]*prod(j=m,p-1,d-j)*
    sum(k=0,floor((p-m)/2),(2*n-p+1)^(p-m-2*k)/(p-m-2*k)!*subst(deltapol[2*k+1],x,d-p))));

  \\ recursion coefficients for G at infinity
  recG=vector(d,p,recF[p+1]-(symvec[2]+d*(s-1)-2*(n-p)-1)/2/d*recF[p]);

  [vector(d-1,p,recF[p+1]),recG]  \\ return them both
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\                 SeriesToContFrac(vec)                                  \\
\\    Convert a power series vec[1]+vec[2]*x+vec[3]*x^2+...               \\
\\    into a continued fraction expansion.                                \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
SeriesToContFrac(vec)=
  local(res=[],ind);
  
  vec=1.*vec;
  while (1,
    res=concat(res,[vec[1]]);
    ind=0;
    until(ind==length(vec) || abs(vec[ind+1])>10^-asympdigits,ind++;vec[ind]=0);
    if(ind>=length(vec),break);
    res=concat(res,[ind]);
    vec=Vec(x^ind/Ser(vec));
  );
  res;
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\                 EvaluateContFrac(cfvec,terms,t)                        \\
\\ Evaluate a continued fraction at x=t, using a given number of terms    \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
EvaluateContFrac(cfvec,terms,t)=
  local(res);

  if (terms<0 || terms>length(cfvec)\2,terms=length(cfvec)\2);
  res=cfvec[2*terms+1];
  while(terms>0,res=if(res,cfvec[2*terms-1]+t^cfvec[2*terms]/res,10^asympdigits);terms--);
  res;
}


\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ cflength( cutoff=1.2 )                                                \\
\\                                                                        \\
\\ number of coefficients necessary to work with L-series with            \\
\\ current Gamma-factor gammaV, conductor, weight and working precision   \\
\\ - CUTOFF specifies largest t used as a cutoff point in checkfeq(t)     \\
\\   and L(...,t,...). Default is 1.2. Set it to 1 if checkfeq()          \\
\\   is not to be used.                                                   \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
cflength(cutoff=1.2)=
  local(vA,d,expdifff,asympconstf,err,t,t1=0,t2=2,tt,res,lfundigits);
  
  vA = sqrt(conductor/Pi^length(gammaV));
  d  = length(gammaV);
  lfundigits = default(realprecision) +
     max(ceil(-log(abs(fullgamma(0.7*weight+MaxImaginaryPart*I)))/log(10)),0);
  expdifff = (sum(k=1,d,gammaV[k])+1)/d-1;
  asympconstf = 2*prod(k=1,d,gamma(k/d));
  err = 10^(-lfundigits-0.5);
  until (t2-t1<=1,
    t  = if(t1,(t1+t2)\2,t2);
    tt = t/cutoff/vA;
    res = coefgrow(t) * asympconstf*exp(-d*tt^(2/d))*tt^expdifff;
    if (t1,if(abs(res)>err,t1=t,t2=t),if(abs(res)<err,t1=t2/2,t2*=2));
  );
  ceil(t2)
}


\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ initLdata(cfstr, cutoff=1.2 [,cfdualstr])                             \\
\\                                                                        \\
\\ - to be called before the L-values computations.                       \\
\\ - gammaV, conductor and weight must be initialized by now.             \\
\\   also coefgrow(), MaxImaginaryPart and MaxAsympCoeffs are used here   \\
\\ - CFSTR must be a string which evaluates to a function of k            \\
\\   which gives k-th coefficient of the L-series, e.g. "(-1)^k"          \\
\\ - CUTOFF specifies largest t used as a cutoff point in checkfeq(t)     \\
\\   and L(...,t,...). Default is 1.2. Set it to 1 if checkfeq()          \\
\\   is not to be used.                                                   \\
\\ - if cutoff<0, force the number of coefficients to be -cutoff          \\
\\ - CFDUALSTR must evaluate (like cfstr) to the k-th coefficient of      \\
\\   the dual L-function if it is different from L(s),                    \\
\\   for instance initLdata("a(k)",,"conj(a(k))")   (see e.g. ex-chgen)   \\
\\ - uses current real precision to determine the desired precision       \\
\\   for L-values                                                         \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
initLdata(vstr,cutoff=1.2,vdualstr="")=
  local(len,d,pordtmp,recF,recG,terms);

  if (type(gammaV)!="t_VEC",
    error("Gamma-factor gammaV has to be defined before calling initLdata()"));
  if (type(conductor)!="t_INT" && type(conductor)!="t_REAL",
    error("conductor has to be defined before calling initLdata()"));

  len=if(cutoff<0,-cutoff,cflength(cutoff));
  cfvec=vector(len,k,eval(vstr));
  if (vdualstr=="",cfdualvec=cfvec,cfdualvec=vector(len,k,eval(vdualstr)));
  if (cutoff<0,len=cflength());

  lastFGs  = -1E99;         \\ globals to track what was calculated last
  lastFGSs = -1E99;         \\ to avoid re-calculating same values each time
  lastGs   = [-1E99,-1E99];
  lastLSs  = [-1E99,-1E99];

  d       = length(gammaV);                   \\ d = number of gamma factors

  \\ Calculate the necessary amount of extra digits

  answerdigits = default(realprecision);
  vA=sqrt(conductor/Pi^d);
  lfundigits = answerdigits +
     max(ceil(-log(abs(fullgamma(0.7*weight+MaxImaginaryPart*I)))/log(10)),0);
  termdigits   = lfundigits + floor(weight-1);
  taylordigits = 2*termdigits;
  asympdigits  = termdigits;

  \\ Exponential factor defined to maximal precision

  default(realprecision,taylordigits);
  vA     = sqrt(precision(conductor,taylordigits)/Pi^d); \\ exp. factor
  lastt  = len/vA;

  pordtmp = vector(d,k,1);                    \\ locate poles and their orders
  for(j=1,d,for(k=1,d,if(j!=k,\
    if(type(diff=gammaV[j]-gammaV[k])=="t_INT" & (diff%2==0) & (diff<=0),\
      pordtmp[j]+=pordtmp[k];pordtmp[k]=0))));
  poles      = [];
  PoleOrders = [];
  for(j=1,d,if(pordtmp[j]!=0,\
    poles=concat(poles,gammaV[j]);PoleOrders=concat(PoleOrders,pordtmp[j])));
  numpoles   = length(poles);

  \\ Initialize the asymptotic coefficients at infinity

  default(realprecision,asympdigits);

  recFG=RecursionsAtInfinity(gammaV);
  recF=recFG[1];
  recG=recFG[2];
  kill(recFG);

  \\ Maximum number of terms in the asymptotic expansion

  ncoeff=MaxAsympCoeffs;

  \\ Asymptotic behaviour at infinity for phi and G

  expdifff    = (sum(k=1,d,gammaV[k])+1)/d-1;
  asympconstf = 2*prod(k=1,d,gamma(k/d));
  expdiffg    = (sum(k=1,d,gammaV[k])+1)/d-1-2/d;
  asympconstg = prod(k=1,d,gamma(k/d));

  \\ Coefficients for the asymptotic expansion of phi(t) and G(t)

  Fvec=vector(d+ncoeff,X,0);
  Fvec[d]=1.;
  for(y=1,ncoeff,Fvec[d+y]=1.*sum(j=1,d-1,subst(recF[j],n,y)*Fvec[d+y-j]));

  Gvec=vector(d+ncoeff,X,0);
  Gvec[d]=1.;
  for(y=1,ncoeff,Gvec[d+y]=1.*sum(j=1,d,subst(recG[j],n,y)*Gvec[d+y-j]));

  \\ Convert the Fvec (Taylor asymptotic) coefficients into fcf (contfrac coeffs)

  fcf=SeriesToContFrac(vector(ncoeff+1,k,Fvec[d+k-1]));
  fncf=length(fcf)\2;     \\ at most ncoeff+1, less if terminates

  \\ Taylor series coefficients of phi(t) around t=infinity

  if (lastt<35,termstep=1,termstep=floor(lastt^(1/3)));
  phiinfterms=vector(round(lastt/termstep)+1,k,-1);

  terms=fncf;
  PhiCaseBound=0;
  for (k=1,length(phiinfterms),
    t1=(k-1)*termstep;
    while ((k>1)&&(terms>1)&&
      (abs(phiinf(t1,terms-1)-phiinf(t1,terms)))<10^(-termdigits-1),terms-=1);
    if (sum(j=1,terms,fcf[2*j])<ncoeff,phiinfterms[k]=terms,PhiCaseBound=k*termstep);
  );

  \\ Recursions for phi(t) and G(t,s) at the origin

  default(realprecision,taylordigits);

  \\ Initial values of the gamma factors for recursions

  InitV = vector(numpoles,j,prod(k=1,d,
          subst(gammaseries((-poles[j]+gammaV[k])/2,PoleOrders[j]-1),x,X/2)));

  \\ Taylor series coefficients of phi(t) around t=0 -> phiVser

  phiV    = [];
  phiVnn  = 0;
  phiVser = InitV;
  until((phiVnn>3)&&(vecmax(abs(phiV[phiVnn]))*((PhiCaseBound+1)*vA)^(2*phiVnn)<10^(-termdigits-1)),
    RecursephiV());

  default(realprecision,answerdigits);
}



\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ phi(t) - inverse Mellin transform of the product of Gamma-factors      \\
\\          computed either with Taylor in 0 or from asymptotics          \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

phi(t)=if(t<PhiCaseBound,phi0(t),phiinf(t,phiinfterms[min(1+floor(abs(t)/termstep),length(phiinfterms))]));

{
RecursephiV()=     \\ compute one more term for the recursions at the origin
  phiVnn++;
  phiV=concat(phiV,[matrix(numpoles,vecmax(PoleOrders),j,k,polcoeff(phiVser[j],-k))]);
  for (j=1,numpoles,for(k=1,length(gammaV),
    phiVser[j]/=(X/2-poles[j]/2-phiVnn+gammaV[k]/2)));
}

{
phi0(t)=                              \\ phi(t) using series expansion at t=0
  local(t2,LogTTerm,TPower,res=0,nn=0,totalold);
  
  default(realprecision,taylordigits);
  t        = precision(t,taylordigits);
  t2       = t^2;
  LogTTerm = vector(vecmax(PoleOrders),k,(-log(t))^(k-1)/(k-1)!)~;
  TPower   = 1.0*vector(numpoles,j,t^poles[j]);
  until (abs(res-totalold)<10^-(termdigits+1)&&(nn>3),
    totalold=res;
    nn++;
    if(nn>phiVnn,RecursephiV());
    res+=TPower*phiV[nn]*LogTTerm;
    TPower*=t2;
  );
  default(realprecision,termdigits);
  res;
}

{                          \\ phi(t) using asymptotic expansion at infinity
phiinf(t,ncf=fncf)=
  local(res,d,td2);
  
  default(realprecision,asympdigits);
  t=precision(t,asympdigits);
  d=length(gammaV);
  td2=t^(-2/d);
  res=EvaluateContFrac(fcf,ncf,td2);
  res=res*asympconstf*exp(-d/td2)*t^expdifff;
  default(realprecision,termdigits);
  res;
}


\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ G(t,s)   - incomplete Mellin transform of phi(t) divided by x^s        \\
\\            computed either with Taylor in 0 or from asymptotics        \\
\\ G(t,s,k) - its k-th derivative (default 0)                             \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
G(t,ss,der=0)= 
  local(nn);

  if(lastGs!=[ss,der] || type(Ginfterms)!="t_VEC",
    initGinf(ss,der);lastGs=[ss,der]);
  if(t<GCaseBound,G0(t,ss,der),
    nn=min(1+floor(abs(t)/termstep),length(Ginfterms));
    Ginf(t,ss,der,Ginfterms[nn]));
}


LogInt(i,j,logt,der)=\
  if (abs(i)<10^(2-termdigits),0,sum(k=0,j-1,binomial((k-j),der)*der!*(-i*logt)^k/k!)/i^(j+der));

{
MakeLogSum(ss,der)=
  local(nn,V,logsum);
  
  if(length(LogSum)==phiVnn,                      \\ more phiV's necessary
    for (j=1,floor(phiVnn/10)+1,RecursephiV()));
  for (nn=length(LogSum)+1,phiVnn,                \\ generate logsums
    V=phiV[nn];
    logsum=vector(numpoles,j,sum(k=1,PoleOrders[j],V[j,k]*LogInt(poles[j]+2*(nn-1)+ss,k,lt,der)))~;
    LogSum=concat(LogSum,[logsum]);
  );
  lastLSs=[ss,der];
}

{
G0(t,ss,der)=                 \\ G(t,s,der) computed using Taylor series at 0
  local(t2,LT,TPower,res,nn,term,gmser,gmcf,dgts);

  default(realprecision,taylordigits);
  ss     = precision(ss,taylordigits);
  if ([ss,der]!=lastLSs,LogSum=[]);
  t      = precision(t,taylordigits);
  t2     = t^2;       \\ time
  LT     = log(t);    \\ = money
  TPower = vector(numpoles,j,t^poles[j]);
  res    = 0;
  nn     = 0;
  term   = 1;
  until ((nn>3) && abs(term)<10^-(termdigits+1),
    nn++;
    if(nn>length(LogSum),MakeLogSum(ss,der));
    term=TPower*subst(LogSum[nn],lt,LT);
    res+=term;
    TPower*=t2;
  );
  gmser=fullgammaseries(ss,der)/t^(S+ss);
  gmcf=polcoeff(gmser,der,S)*der!;
  res=(gmcf-res);
  default(realprecision,termdigits);
  res;
}


{
Ginf(t,ss,der,ncf=-1)=     \\ G(t,s,der) computed using asymptotic expansion
  local(res,d,tt);         \\ at infinity and associated continued fraction
  
  default(realprecision,asympdigits);
  ss=precision(ss,asympdigits);
  t=precision(t,asympdigits);
  if (ncf==-1,ncf=gncf);
  d=length(gammaV);
  tt=t^(-2/d);
  res=EvaluateContFrac(gcf,ncf,tt);
  res=asympconstg*exp(-d/tt)*t^expdiffg*tt^der*res;
  default(realprecision,termdigits);
  res;
}


{
initGinf(ss,der)=          \\ pre-compute asymptotic expansions for a given s

  local(d,gvec,gncf,terms,t1);
  default(realprecision,asympdigits);
  ss=precision(ss,asympdigits);
  d=length(gammaV);
  gvec=Gvec;
  for (k=1,der,gvec=deriv(gvec,s);gvec=concat(vecright(gvec,length(gvec)-1),1));
  gcf=SeriesToContFrac(vector(ncoeff+1,k,subst(gvec[d+k-1],s,ss)));
  gncf=length(gcf)\2;
  Ginfterms=vector(round(lastt/termstep)+1,k,-1);
  terms=gncf;
  GCaseBound=0;
  for (k=1,length(Ginfterms),
    t1=(k-1)*termstep;
    while ((k>1)&&(terms>1)&&
      (abs(Ginf(t1,ss,der,terms-1)-Ginf(t1,ss,der,terms)))<10^(-termdigits-2),terms-=1);
    if (sum(j=1,terms,gcf[2*j])<ncoeff,Ginfterms[k]=terms,GCaseBound=k*termstep);
  );
  default(realprecision,termdigits);
}


\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ ltheta(t) = sum a(k)*phi(k*t/vA)                                        \\
\\ satisfies ltheta(1/t)=sgn*t^weight*ldualtheta(t) + residue contribution \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

ltheta(t) = t=precision(t/vA,taylordigits);\
  sum(k=1,min(floor(lastt/t+1),length(cfvec)),cfvec[k]*if(cfvec[k],phi(k*t),0));
ldualtheta(t) = t=precision(t/vA,taylordigits);\
  sum(k=1,min(floor(lastt/t+1),length(cfvec)),cfdualvec[k]*if(cfdualvec[k],phi(k*t),0));

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ checkfeq(t=1.2) = verify the functional equation by evaluating LHS-RHS  \\
\\                   for func.eq for ltheta(t), should return approx. 0    \\
\\ - also determines residues if Lresidues is set to automatic             \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

{
checkfeq(t=6/5)=                \\ determine residues if they are not yet set
  local(nlp,lpx,lpv,lpm,res);   \\ .. and check the functional equation
  
  if (Lresidues==automatic && Lpoles!=[],
    nlp=length(Lpoles);
    lpx=vector(nlp,k,1.15+if(k==1,0,k-1)/10);
    lpv=vector(nlp,k,tq=lpx[k];sgn*tq^weight*ldualtheta(tq)-ltheta(1/tq));
    lpm=matrix(nlp,nlp,k,j,tq=lpx[k];ss=Lpoles[j];tq^ss-sgn*tq^(weight-ss));
    Lresidues=matsolve(lpm,lpv~)~;
  );
  res=ltheta(t)-sgn*t^(-weight)*ldualtheta(1/t)+
    sum(k=1,length(Lpoles),Lresidues[k]*(t^-Lpoles[k]-sgn*t^(-weight+Lpoles[k])));
  default(realprecision,answerdigits);
  res;
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ L(ss,cutoff,k) = k-th derivative of L(s) at s=ss                       \\
\\                  cutoff = 1 by default (cutoff point), >=1 in general  \\
\\                  must be equal to 1 if k>0                             \\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

L(ss,cutoff=1,der=0)=polcoeff(Lseries(ss,cutoff,der),der)*der!


{       \\ Lseries(s,,der) = L(s)+L'(s)S+L''(s)*S^2/2!+... ,first der terms
Lseries(ss,cutoff=1,der=0)=
    
  local(FGSeries,LGSeries,res);
  
  default(realprecision,lfundigits);
  FGSeries = fullgammaseries(ss,der)*vA^(ss+S);
  if (length(Lpoles) && (vecmin(abs(vector(length(Lpoles),k,Lpoles[k]-ss)))<10^(-answerdigits) ||
    vecmin(abs(vector(length(Lpoles),k,weight-Lpoles[k]-ss)))<10^(-answerdigits)),
    error("L*(s) has a pole at s=",ss));
\\  if (vecmin(abs(vector(length(Lpoles),k,Lpoles[k]-ss)))<10^(-answerdigits) ||
\\    vecmin(abs(vector(length(Lpoles),k,weight-Lpoles[k]-ss)))<10^(-answerdigits),
\\    error("L*(s) has a pole at s=",ss));
  PrecisionLoss = ceil(-log(vecmax(abs(Vec(FGSeries))))/log(10))
    -(lfundigits-answerdigits);
  if (PrecisionLoss>1,
    print("Warning: Loss of ",PrecisionLoss," digits due to cancellation"));
  LSSeries = sum(k=0,der,Lstar(ss,cutoff,k)*S^k/k!)+O(S^(der+1));
  res=LSSeries/FGSeries;
  default(realprecision,answerdigits);
  res;
}


{
Lstar(ss,cutoff=1,der=0)=                \\ Lstar(s) = L(s) * Gamma factor
  local(res,ncf1,ncf2);

  if (der & (cutoff!=1),error("L(s,cutoff,k>0) is only implemented for cutoff=1"));
  ss     = precision(ss,taylordigits);
  cutoff = precision(cutoff,taylordigits);
  ncf1   = min(round(lastt*vA*cutoff),length(cfvec));
  ncf2   = min(round(lastt*vA/cutoff),length(cfvec));
  default(realprecision,termdigits);
  res=(-sum(k=1,length(Lpoles),(-1)^der*der!*Lresidues[k]/(ss-Lpoles[k])^(der+1)*cutoff^(-Lpoles[k]))
       -sgn*sum(k=1,length(Lpoles),Lresidues[k]*der!/(weight-Lpoles[k]-ss)^(der+1)*cutoff^(-weight+Lpoles[k]))
       +sgn*sum(k=1,ncf1,if(cfdualvec[k],cfdualvec[k]*(-1)^der*G(k/vA/cutoff,weight-ss,der),0)/cutoff^weight)
       +sum(k=1,ncf2,if(cfvec[k],cfvec[k]*G(k*cutoff/vA,ss,der),0))
  )*cutoff^ss;
  res;
}