File indexing completed on 2024-05-12 17:22:33

0001 /* floatgamma.c: Gamma function, based on floatnum. */
0002 /*
0003     Copyright (C) 2007, 2008 Wolf Lammen.
0004 
0005     This program is free software; you can redistribute it and/or modify
0006     it under the terms of the GNU General Public License as published by
0007     the Free Software Foundation; either version 2 of the License , or
0008     (at your option) any later version.
0009 
0010     This program is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013     GNU General Public License for more details.
0014 
0015     You should have received a copy of the GNU General Public License
0016     along with this program; see the file COPYING.  If not, write to:
0017 
0018       The Free Software Foundation, Inc.
0019       59 Temple Place, Suite 330
0020       Boston, MA 02111-1307 USA.
0021 
0022 
0023     You may contact the author by:
0024        e-mail:  ookami1 <at> gmx <dot> de
0025        mail:  Wolf Lammen
0026               Oertzweg 45
0027               22307 Hamburg
0028               Germany
0029 
0030 *************************************************************************/
0031 
0032 #include "floatgamma.h"
0033 #include "floatconst.h"
0034 #include "floatcommon.h"
0035 #include "floatlog.h"
0036 #include "floatexp.h"
0037 #include "floattrig.h"
0038 
0039 /* asymptotic series of the Binet function
0040    for x >= 77 and a 100 digit computation, the
0041    relative error is < 9e-100.
0042    the series converges, if x and digits comply to
0043      100 >= digits >= 2
0044      x >= sqrt((digits*ln 10 + 0.5*ln 2)/1.0033).
0045    As a special case, for digits == 1, convergence is guaranteed,
0046    if x >= 1.8. */
0047 
0048 char
0049 binetasymptotic(floatnum x,
0050                 int digits)
0051 {
0052   floatstruct recsqr;
0053   floatstruct sum;
0054   floatstruct smd;
0055   floatstruct pwr;
0056   int i, workprec;
0057 
0058   if (float_getexponent(x) >= digits)
0059   {
0060     /* if x is very big, ln(gamma(x)) is
0061     dominated by x*ln x and the Binet function
0062     does not contribute anything substantial to
0063     the final result */
0064     float_setzero(x);
0065     return 1;
0066   }
0067   float_create(&recsqr);
0068   float_create(&sum);
0069   float_create(&smd);
0070   float_create(&pwr);
0071 
0072   float_copy(&pwr, &c1, EXACT);
0073   float_setzero(&sum);
0074   float_div(&smd, &c1, &c12, digits+1);
0075   workprec = digits - 2*float_getexponent(x)+3;
0076   i = 1;
0077   if (workprec > 0)
0078   {
0079     float_mul(&recsqr, x, x, workprec);
0080     float_reciprocal(&recsqr, workprec);
0081     while (float_getexponent(&smd) > -digits-1
0082            && ++i <= MAXBERNOULLIIDX)
0083     {
0084       workprec = digits + float_getexponent(&smd) + 3;
0085       float_add(&sum, &sum, &smd, digits+1);
0086       float_mul(&pwr, &recsqr, &pwr, workprec);
0087       float_muli(&smd, &cBernoulliDen[i-1], 2*i*(2*i-1), workprec);
0088       float_div(&smd, &pwr, &smd, workprec);
0089       float_mul(&smd, &smd, &cBernoulliNum[i-1], workprec);
0090     }
0091   }
0092   else
0093     /* sum reduces to the first summand*/
0094     float_move(&sum, &smd);
0095   if (i > MAXBERNOULLIIDX)
0096       /* x was not big enough for the asymptotic
0097     series to converge sufficiently */
0098     float_setnan(x);
0099   else
0100     float_div(x, &sum, x, digits);
0101   float_free(&pwr);
0102   float_free(&smd);
0103   float_free(&sum);
0104   float_free(&recsqr);
0105   return i <= MAXBERNOULLIIDX;
0106 }
0107 
0108 /* returns the number of summands needed in the asymptotic
0109    series to guarantee <digits> precision. Each extra summand
0110    yields roughly extra 1.8 digits. This is derived under the
0111    assumption, that the costs of an extra factor in the rising
0112    pochhammer symbol are about the same than those of an extra
0113    summand in the series */
0114 static int
0115 _findorder(
0116   int digits)
0117 {
0118   return (5*digits + 5)/9;
0119 }
0120 
0121 /* returns how big x has to be to let the asymptotic series
0122    converge to at least <digits> precision. Derived from
0123    an estimation of the Bernouilli number inserted in the
0124    formula of a summand. */
0125 static int
0126 _minx(
0127   int digits)
0128 {
0129   return (4657*_findorder(digits)-2750)/5000;
0130 }
0131 
0132 /* returns how much x has to be increased to let the
0133    asymptotic series converge to <digits> places */
0134 static int
0135 _ofs(
0136   floatnum x,
0137   int digits)
0138 {
0139   int result;
0140 
0141   if (float_getexponent(x) >= 8)
0142     return 0;
0143   result = _minx(digits) - float_asinteger(x);
0144   return result <= 0? 0 : result;
0145 }
0146 
0147 /* evaluates the rising pochhammer symbol x*(x+1)*...*(x+n-1)
0148    (n >= 0) by multiplying. This can be expensive when n is large,
0149    so better restrict n to something sane like n <= 100.
0150    su stands for "small" and "unsigned" n */
0151 static char
0152 _pochhammer_su(
0153   floatnum x,
0154   int n,
0155   int digits)
0156 {
0157   floatstruct factor;
0158   char result;
0159 
0160   /* the rising pochhammer symbol is computed recursively,
0161   observing that
0162   pochhammer(x, n) == pochhammer(x, p) * pochhammer(x+p, n-p).
0163   p is choosen as floor(n/2), so both factors are somehow
0164   "balanced". This pays off, if x has just a few digits, since
0165   only some late multiplications are full scale then and
0166   Karatsuba boosting yields best results, because both factors
0167   are always almost the same size. */
0168 
0169   result = 1;
0170   switch (n)
0171   {
0172   case 0:
0173     float_copy(x, &c1, EXACT);
0174   case 1:
0175     break;
0176   default:
0177     float_create(&factor);
0178     float_addi(&factor, x, n >> 1, digits+2);
0179     result = _pochhammer_su(x, n >> 1, digits)
0180         && _pochhammer_su(&factor, n - (n >> 1), digits)
0181         && float_mul(x, x, &factor, digits+2);
0182     float_free(&factor);
0183   }
0184   return result;
0185 }
0186 
0187 /* evaluates ln(Gamma(x)) for all those x big
0188    enough to let the asymptotic series converge directly.
0189    Returns 0, if the result overflows
0190    relative error for a 100 gigit calculation < 5e-100 */
0191 static char
0192 _lngammabigx(
0193   floatnum x,
0194   int digits)
0195 {
0196   floatstruct tmp1, tmp2;
0197   char result;
0198 
0199   result = 0;
0200   float_create(&tmp1);
0201   float_create(&tmp2);
0202   /* compute (ln x-1) * (x-0.5) - 0.5 + ln(sqrt(2*pi)) */
0203   float_copy(&tmp2, x, digits+1);
0204   _ln(&tmp2, digits+1);
0205   float_sub(&tmp2, &tmp2, &c1, digits+2);
0206   float_sub(&tmp1, x, &c1Div2, digits+2);
0207   if (float_mul(&tmp1, &tmp1, &tmp2, digits+2))
0208   {
0209     /* no overflow */
0210     binetasymptotic(x, digits);
0211     float_add(x, &tmp1, x, digits+3);
0212     float_add(x, x, &cLnSqrt2PiMinusHalf, digits+3);
0213     result = 1;
0214   }
0215   float_free(&tmp2);
0216   float_free(&tmp1);
0217   return result;
0218 }
0219 
0220 static char
0221 _lngamma_prim_xgt0(
0222   floatnum x,
0223   floatnum revfactor,
0224   int digits)
0225 {
0226   int ofs;
0227 
0228   ofs = _ofs(x, digits);
0229   float_copy(revfactor, x, digits+1);
0230   _pochhammer_su(revfactor, ofs, digits);
0231   float_addi(x, x, ofs, digits+2);
0232   return _lngammabigx(x, digits);
0233 }
0234 
0235 static char
0236 _lngamma_prim(
0237   floatnum x,
0238   floatnum revfactor,
0239   int* infinity,
0240   int digits)
0241 {
0242   floatstruct tmp;
0243   char result;
0244   char odd;
0245 
0246   *infinity = 0;
0247   if (float_getsign(x) > 0)
0248     return _lngamma_prim_xgt0(x, revfactor, digits);
0249   float_copy(revfactor, x, digits + 2);
0250   float_sub(x, &c1, x, digits+2);
0251   float_create(&tmp);
0252   result = _lngamma_prim_xgt0(x, &tmp, digits);
0253   if (result)
0254   {
0255     float_neg(x);
0256     odd = float_isodd(revfactor);
0257     _sinpix(revfactor, digits);
0258     if (float_iszero(revfactor))
0259     {
0260       *infinity = 1;
0261       float_setinteger(revfactor, odd? -1 : 1);
0262     }
0263     else
0264       float_mul(&tmp, &tmp, &cPi, digits+2);
0265     float_div(revfactor, revfactor, &tmp, digits+2);
0266   }
0267   float_free(&tmp);
0268   return result;
0269 }
0270 
0271 char
0272 _lngamma(
0273   floatnum x,
0274   int digits)
0275 {
0276   floatstruct factor;
0277   int infinity;
0278   char result;
0279 
0280   if (float_cmp(x, &c1) == 0 || float_cmp(x, &c2) == 0)
0281     return _setzero(x);
0282   float_create(&factor);
0283   result = _lngamma_prim(x, &factor, &infinity, digits)
0284            && infinity == 0;
0285   if (result)
0286   {
0287     float_abs(&factor);
0288     _ln(&factor, digits + 1);
0289     result = float_sub(x, x, &factor, digits+1);
0290   }
0291   float_free(&factor);
0292   if (infinity != 0)
0293     return _seterror(x, ZeroDivide);
0294   if (!result)
0295     float_setnan(x);
0296   return result;
0297 }
0298 
0299 char
0300 _gammagtminus20(
0301   floatnum x,
0302   int digits)
0303 {
0304   floatstruct factor;
0305   int ofs;
0306   char result;
0307 
0308   float_create(&factor);
0309   ofs = _ofs(x, digits+1);
0310   float_copy(&factor, x, digits+1);
0311   _pochhammer_su(&factor, ofs, digits);
0312   float_addi(x, x, ofs, digits+2);
0313   result = _lngammabigx(x, digits)
0314            && _exp(x, digits)
0315            && float_div(x, x, &factor, digits+1);
0316   float_free(&factor);
0317   if (!result)
0318     float_setnan(x);
0319   return result;
0320 }
0321 
0322 char
0323 _gamma(
0324   floatnum x,
0325   int digits)
0326 {
0327   floatstruct tmp;
0328   int infinity;
0329   char result;
0330 
0331   if (float_cmp(&cMinus20, x) > 0)
0332   {
0333     float_create(&tmp);
0334     result = _lngamma_prim(x, &tmp, &infinity, digits)
0335              && infinity == 0
0336              && _exp(x, digits)
0337              && float_div(x, x, &tmp, digits + 1);
0338     float_free(&tmp);
0339     if (infinity != 0)
0340       return _seterror(x, ZeroDivide);
0341     if (!result)
0342       float_setnan(x);
0343     return result;
0344   }
0345   return _gammagtminus20(x, digits);
0346 }
0347 
0348 char
0349 _gammaint(
0350   floatnum integer,
0351   int digits)
0352 {
0353   int ofs;
0354 
0355   if (float_getexponent(integer) >=2)
0356     return _gammagtminus20(integer, digits);
0357   ofs = float_asinteger(integer);
0358   float_copy(integer, &c1, EXACT);
0359   return _pochhammer_su(integer, ofs-1, digits);
0360 }
0361 
0362 char
0363 _gamma0_5(
0364   floatnum x,
0365   int digits)
0366 {
0367   floatstruct tmp;
0368   int ofs;
0369 
0370   if (float_getexponent(x) >= 2)
0371     return _gamma(x, digits);
0372   float_create(&tmp);
0373   float_sub(&tmp, x, &c1Div2, EXACT);
0374   ofs = float_asinteger(&tmp);
0375   float_free(&tmp);
0376   if (ofs >= 0)
0377   {
0378     float_copy(x, &c1Div2, EXACT);
0379     if(!_pochhammer_su(x, ofs, digits))
0380       return 0;
0381     return float_mul(x, x, &cSqrtPi, digits);
0382   }
0383   if(!_pochhammer_su(x, -ofs, digits))
0384     return 0;
0385   return float_div(x, &cSqrtPi, x, digits);
0386 }
0387 
0388 static char
0389 _pochhammer_si(
0390   floatnum x,
0391   int n,
0392   int digits)
0393 {
0394   /* this extends the rising Pochhammer symbol to negative integer offsets
0395      following the formula pochhammer(x,n-1) = pochhammer(x,n)/(x-n+1) */
0396 
0397   if (n >= 0)
0398     return _pochhammer_su(x, n, digits);
0399   return float_addi(x, x, n, digits)
0400          && _pochhammer_su(x, -n, digits)
0401          && float_reciprocal(x, digits);
0402 }
0403 
0404 static char
0405 _pochhammer_g(
0406   floatnum x,
0407   cfloatnum n,
0408   int digits)
0409 {
0410   /* this generalizes the rising Pochhammer symbol using the
0411      formula pochhammer(x,n) = Gamma(x+1)/Gamma(x-n+1) */
0412   floatstruct tmp, factor1, factor2;
0413   int inf1, inf2;
0414   char result;
0415 
0416   float_create(&tmp);
0417   float_create(&factor1);
0418   float_create(&factor2);
0419   inf2 = 0;
0420   float_add(&tmp, x, n, digits+1);
0421   result = _lngamma_prim(x, &factor1, &inf1, digits)
0422            && _lngamma_prim(&tmp, &factor2, &inf2, digits)
0423            && (inf2 -= inf1) <= 0;
0424   if (inf2 > 0)
0425     float_seterror(ZeroDivide);
0426   if (result && inf2 < 0)
0427     float_setzero(x);
0428   if (result && inf2 == 0)
0429     result = float_div(&factor1, &factor1, &factor2, digits+1)
0430              && float_sub(x, &tmp, x, digits+1)
0431              && _exp(x, digits)
0432              && float_mul(x, x, &factor1, digits+1);
0433   float_free(&tmp);
0434   float_free(&factor2);
0435   float_free(&factor1);
0436   if (!result)
0437     float_setnan(x);
0438   return result;
0439 }
0440 
0441 static char
0442 _pochhammer_i(
0443   floatnum x,
0444   cfloatnum n,
0445   int digits)
0446 {
0447   /* do not use the expensive Gamma function when a few
0448      multiplications do the same */
0449   /* pre: n is an integer */
0450   int ni;
0451   signed char result;
0452 
0453   if (float_iszero(n))
0454     return float_copy(x, &c1, EXACT);
0455   if (float_isinteger(x))
0456   {
0457     result = -1;
0458     float_neg((floatnum)n);
0459     if (float_getsign(x) <= 0 && float_cmp(x, n) > 0)
0460       /* x and x+n have opposite signs, meaning 0 is
0461          among the factors */
0462       result = _setzero(x);
0463     else if (float_getsign(x) > 0 && float_cmp(x, n) <= 0)
0464       /* x and x+n have opposite signs, meaning at one point
0465       you have to divide by 0 */
0466       result = _seterror(x, ZeroDivide);
0467     float_neg((floatnum)n);
0468     if (result >= 0)
0469       return result;
0470   }
0471   if (float_getexponent(x) < EXPMAX/100)
0472   {
0473     ni = float_asinteger(n);
0474     if (ni != 0 && ni < 50 && ni > -50)
0475       return _pochhammer_si(x, ni, digits+2);
0476   }
0477   return _pochhammer_g(x, n, digits);
0478 }
0479 
0480 char
0481 _pochhammer(
0482   floatnum x,
0483   cfloatnum n,
0484   int digits)
0485 {
0486   if (float_isinteger(n))
0487     return _pochhammer_i(x, n, digits);
0488   return _pochhammer_g(x, n, digits);
0489 }