/* LA: linear algebra C++ interface library Copyright (C) 2024 Jiri Pittner or This program 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 3 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, see . */ #include #include #include #include #include "miscfunc.h" #include "laerror.h" #include "polynomial.h" namespace LA { #define MAXFACT 20 unsigned long long factorial(const int n) { static int ntop=20; static unsigned long long a[MAXFACT+1]={1,1,2,6,24,120,720,5040, 40320, 362880, 3628800, 39916800ULL, 479001600ULL, 6227020800ULL, 87178291200ULL, 1307674368000ULL, 20922789888000ULL, 355687428096000ULL, 6402373705728000ULL, 121645100408832000ULL, 2432902008176640000ULL}; int j; if (n < 0) laerror("negative argument of factorial"); if (n > MAXFACT) laerror("overflow in factorial"); while (ntop MAXDOUBLEFACT) laerror("overflow in double_factorial"); if (n <= MAXFACT) return (double)factorial(n); if (ntop == MAXFACT) a[MAXFACT]=(double)factorial(MAXFACT); while (ntop=2.0) /*does not have sense for smaller anyway and could make troubles*/ { int i,ii,m; m=(int)std::ceil(0.5-std::log(EPS/10000.)/logz/2.0); if(m<2)m=2; if(m>16)m=16; nn=z*z; for(i=m;i>0;i--) {ii=2*i;cor = cor/nn + bernoulli_number(ii)/((double)ii*(ii-1));} cor /=z; } /*0.5*ln(2*M_PI)=0.91893853320467275836*/ return((z-0.5)*logz-z+0.918938533204672741780329736406+cor); /*cor=std::log((1.0/(24.0*n)+1.0)/(12.0*n)+1.0)*/ } #define LIMITFACT 180 double factorialln(int n) { static int top=MAXFACT; static double a[LIMITFACT+1]={ 0.0, 0.0, 0.69314718055994530843, 1.7917594692280549573, 3.1780538303479457518, 4.7874917427820458116, 6.579251212010101213, 8.5251613610654146669, 10.604602902745250859, 12.801827480081469091, 15.10441257307551588, 17.502307845873886549, 19.987214495661884683, 22.552163853123424531, 25.191221182738679829, 27.899271383840890337, 30.671860106080671926, 33.505073450136890756, 36.395445208033052609, 39.339884187199494647, 42.335616460753485057, 45.380138898476907627, 48.471181351835227247, 51.606675567764376922, 54.784729398112318677, 58.003605222980517908, 61.261701761002001376, 64.557538627006337606, 67.889743137181540078, 71.257038967168014665, 74.658236348830158136}; if(n < 0) laerror("negative argument of factln"); if(n < 2) return(0.0); if(n <= LIMITFACT) /*supposed LIMITFACT > MAXFACT*/ if(a[n]) return(a[n]); else { if(n<=MAXFACT) return(a[n]= std::log(factorial(n))); { int j; if(!a[top]) a[top]= std::log(factorial(top)); while (topn||k<0) return(0); if(k>n/2) k=n-k; if(k==0) return(1); if(k==1) return(n); int ind=0; if(n<=MAXBINOM) { ind=ibidx(n,k); if (ibitab[ind]) return ibitab[ind]; } /* nonrecurent method used anyway */ /* value = C(n-k+i,i) after step i is exact; divide by the common factor first to avoid overflow of intermediates */ value=1; for(int i=1; i<=k; ++i) { unsigned long long m=n-k+i; unsigned long long g=ullgcd(m,i); m /= g; value /= (i/g); /*exact, since value*m is divisible by i and m/g is coprime to i/g*/ if(value > std::numeric_limits::max()/m) laerror("overflow in binom"); value *= m; } if(n<=MAXBINOM) ibitab[ind]=value; return value; } #undef ibidx #undef ibidxmaxeven #undef MAXBINOM unsigned long long longpow(unsigned long long x, int i) { if(i<0) return 0; unsigned long long y=1; do { if(i&1) y *= x; i >>= 1; if(i) x *= x; } while(i); return y ; } /* Euler-Maclaurin summation: zeta(x)= sum_{k=1}^{N-1} k^-x + N^(1-x)/(x-1) + N^-x/2 + sum_{j=1}^m B_2j/(2j)! x(x+1)...(x+2j-2) N^(-x-2j+1) */ /* accurate to about 1e-15 for -1.02<=x<9.1 (the pole term 1/(x-1) is exact) */ static double zeta_euler_maclaurin(double x) { const int N=10, m=10; double s,nx,t; int j,k; s=0.0; for(k=1;k=9.1) /* in this region the summation per definition is best and quite exact*/ { double s; int i,n; n=(int)std::ceil(pow(EPS/1000.0,-1.0/x)); s=0.0;for(i=n;i>1;i--) s+= pow((double)i,-x); return(1.0+s); } return zeta_euler_maclaurin(x); } //end zeta double gamma(double x) { bool f; double p; if(x>MAXDOUBLEFACT+1) laerror("too big argument in gamma"); f= (std::fabs(x-std::floor(x+0.5))= MAXBERN) /* use zeta function for it */ {int m; m=2*n; return(zeta((double)m)*(n&1?2.0:-2.0)*double_factorial(m)/pow(2.0*M_PI,(double)m)); } if(n<=top) return(bern[n]); /* this is very unexact - recurrent formula with binom. coef. for(i=top+1;i<=n;i++) { int j,d; double s; d=2*i+1; s=0.0; for(j=1;jmaxn) { if(n>=SIMPLICIAL_MAXN) laerror("storage overflow in simplicial, increase MAXN"); int newdim=2*n; if(newdim<2*maxn) newdim=2*maxn; if(newdim>=SIMPLICIAL_MAXN) newdim=SIMPLICIAL_MAXN-1; for(int i=maxn+1; i<=newdim;++i) stored[0][i] = i*(i+1)/2; for(int dd=1; dd<=maxd-2; ++dd) { for(int i=maxn+1; i<=newdim;++i) stored[dd][i] = stored[dd][i-1] + stored[dd-1][i]; } maxn=newdim; } if(d>maxd) { if(d>=SIMPLICIAL_MAXD) laerror("storage overflow in simplicial, increase MAXD"); for(int dd=maxd+1; dd<=d; ++dd) { stored[dd-2][0]=0; stored[dd-2][1]=1; for(int i=2; i<=maxn; ++i) stored[dd-2][i] = stored[dd-3][i] + stored[dd-2][i-1]; } maxd=d; } return stored[d-2][n]; } //find largest n such that simplicial(d,n)<=s int inverse_simplicial(int d, unsigned long long s) { if(s==0) return 0; if(d==0 || s==1) return 1; if(d==1) return (int)s; if(d>=SIMPLICIAL_MAXD) laerror("storage overflow in inverse_simplicial - increase MAXD"); static int maxd=0; static Polynomial polynomials[SIMPLICIAL_MAXD]; static Polynomial polynomials_der[SIMPLICIAL_MAXD]; for(int i=maxd+1; i<=d; ++i) //generate as needed { NRVec roots(i); for(int j=0; j.5); //so usually 2 iterations are enough int n= std::floor(x); while(simplicial(d,n)>s) --n; return n; } #undef SIMPLICIAL_MAXD #undef SIMPLICIAL_MAXN }//namespace