Example #1
0
        // one-argument functions

        /// <summary>
        /// Computes the natural logarithm of the Gamma function.
        /// </summary>
        /// <param name="x">The argument, which must be positive.</param>
        /// <returns>The log Gamma function ln(&#x393;(x)).</returns>
        /// <remarks>
        /// <para>Because &#x393;(x) grows rapidly for increasing positive x, it is often necessary to
        /// work with its logarithm in order to avoid overflow. This function returns accurate
        /// values of ln(&#x393;(x)) even for values of x which would cause &#x393;(x) to overflow.</para>
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="x"/> is negative.</exception>
        /// <seealso cref="Gamma(double)" />
        public static double LogGamma(double x)
        {
            if (x < 0.0)
            {
                throw new ArgumentOutOfRangeException(nameof(x));
            }
            else if (x < 16.0)
            {
                // For small arguments, use the Lanczos approximation.
                return(Lanczos.LogGamma(x));
            }
            else if (x < Double.PositiveInfinity)
            {
                // For large arguments, the asymptotic series is even faster than the Lanczos approximation.
                return(Stirling.LogGamma(x));
            }
            else if (x == Double.PositiveInfinity)
            {
                // Precisely at infinity x * Math.Log(x) - x => NaN, so special-case it.
                return(Double.PositiveInfinity);
            }
            else
            {
                return(Double.NaN);
            }
        }
Example #2
0
        // one-argument functions

        /// <summary>
        /// Computes the natural logrithm of the Gamma function.
        /// </summary>
        /// <param name="x">The argument, which must be positive.</param>
        /// <returns>The log Gamma function ln(&#x393;(x)).</returns>
        /// <remarks>
        /// <para>Because &#x393;(x) grows rapidly for increasing positive x, it is often necessary to
        /// work with its logarithm in order to avoid overflow. This function returns accurate
        /// values of ln(&#x393;(x)) even for values of x which would cause &#x393;(x) to overflow.</para>
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="x"/> is negative or zero.</exception>
        /// <seealso cref="Gamma(double)" />
        public static double LogGamma(double x)
        {
            if (x <= 0.0)
            {
                throw new ArgumentOutOfRangeException("x");
            }
            else if (x < 16.0)
            {
                // For small arguments, use the Lanczos approximation.
                return(Lanczos.LogGamma(x));
            }
            else
            {
                // For large arguments, the asymptotic series is even faster than the Lanczos approximation.
                return(Stirling.LogGamma(x));
                //return (LogGamma_Stirling(x));
            }
        }
Example #3
0
        // one-argument functions

        /// <summary>
        /// Computes the natural logarithm of the Gamma function.
        /// </summary>
        /// <param name="x">The argument, which must be positive.</param>
        /// <returns>The log Gamma function ln(&#x393;(x)).</returns>
        /// <remarks>
        /// <para>Because &#x393;(x) grows rapidly for increasing positive x, it is often necessary to
        /// work with its logarithm in order to avoid overflow. This function returns accurate
        /// values of ln(&#x393;(x)) even for values of x which would cause &#x393;(x) to overflow.</para>
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="x"/> is negative.</exception>
        /// <seealso cref="Gamma(double)" />
        public static double LogGamma(double x)
        {
            if (x < 0.0)
            {
                throw new ArgumentOutOfRangeException(nameof(x));
            }
            else if (x < 0.5)
            {
                // For small arguments, use the Lanczos approximation.
                return(Lanczos.LogGamma(x));
            }
            else if (x < 1.5)
            {
                // Use the series expansion near 1.
                return(GammaSeries.LogGammaOnePlus(x - 1.0));
            }
            else if (x < 2.5)
            {
                // The series expansion can be adapted near 2, too.
                return(GammaSeries.LogGammaTwoPlus(x - 2.0));
            }
            else if (x < 16.0)
            {
                // In between, we still use Lanczos.
                return(Lanczos.LogGamma(x));
            }
            else if (x < Double.PositiveInfinity)
            {
                // For large arguments, the asymptotic series is even faster than the Lanczos approximation.
                return(Stirling.LogGamma(x));
            }
            else if (x == Double.PositiveInfinity)
            {
                // Precisely at infinity x * Math.Log(x) - x => NaN, so special-case it.
                return(Double.PositiveInfinity);
            }
            else
            {
                return(Double.NaN);
            }
        }
Example #4
0
 /// <summary>
 /// Compute the complex log Gamma function.
 /// </summary>
 /// <param name="z">The complex argument.</param>
 /// <returns>The principal complex value y for which exp(y) = &#x393;(z).</returns>
 /// <seealso cref="AdvancedMath.LogGamma" />
 /// <seealso href="http://mathworld.wolfram.com/LogGammaFunction.html"/>
 public static Complex LogGamma(Complex z)
 {
     if (z.Im == 0.0 && z.Re < 0.0)
     {
         // Handle the pure negative case explicitly.
         double re = Math.Log(Math.PI / Math.Abs(MoreMath.SinPi(z.Re))) - AdvancedMath.LogGamma(1.0 - z.Re);
         double im = Math.PI * Math.Floor(z.Re);
         return(new Complex(re, im));
     }
     else if (z.Re > 16.0 || Math.Abs(z.Im) > 16.0)
     {
         // According to https://dlmf.nist.gov/5.11, the Stirling asymptoic series is valid everywhere
         // except on the negative real axis. So at first I tried to use it for |z.Im| > 0, |z| > 16. But in practice,
         // it exhibits false convergence close to the negative real axis, e.g. z = -16 + i. So I have
         // moved to requiring |z| large and reasonably far from the negative real axis.
         return(Stirling.LogGamma(z));
     }
     else if (z.Re >= 0.125)
     {
         return(Lanczos.LogGamma(z));
     }
     else
     {
         // For the remaining z < 0, we need to use the reflection formula.
         // For large z.Im, SinPi(z) \propto e^{\pi |z.Im|} overflows even though its log does not.
         // It's possible to do some algebra to get around that problem, but it's not necessary
         // because for z.Im that big we would have used the Stirling series.
         Complex f = ComplexMath.Log(Math.PI / ComplexMath.SinPi(z));
         Complex g = Lanczos.LogGamma(1.0 - z);
         // The reflection formula doesn't stay on the principal branch, so we need to add a multiple of 2 \pi i
         // to fix it up. See Hare, "Computing the Principal Branch of Log Gamma" for how to do this.
         // https://pdfs.semanticscholar.org/1c9d/8865836a312836500126cb47c3cbbed3043e.pdf
         Complex h = new Complex(0.0, 2.0 * Math.PI * Math.Floor(0.5 * (z.Re + 0.5)));
         if (z.Im < 0.0)
         {
             h = -h;
         }
         return(f - g + h);
     }
 }