/*
 * Copyright 2003-2004 
The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package util.math;

import java.io.Serializable;

/**
 * This is a utility class that provides computation methods related to the
 * Gamma family of functions.
 *
 * @version $Revision: 1.22 $ $Date: 2004/10/08 05:53:18 $
 */
public class Gamma implements Serializable {
    
    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/** Maximum allowed numerical error. */
    @SuppressWarnings("unused")
	private static final double DEFAULT_EPSILON = 10e-9;

    /** Lanczos coefficients */
    private static double[] lanczos =
    {
        0.99999999999999709182,
        57.156235665862923517,
        -59.597960355475491248,
        14.136097974741747174,
        -0.49191381609762019978,
        .33994649984811888699e-4,
        .46523628927048575665e-4,
        -.98374475304879564677e-4,
        .15808870322491248884e-3,
        -.21026444172410488319e-3,
        .21743961811521264320e-3,
        -.16431810653676389022e-3,
        .84418223983852743293e-4,
        -.26190838401581408670e-4,
        .36899182659531622704e-5,
    };

    /** Avoid repeated computation of log of 2 PI in logGamma */
    private static final double HALF_LOG_2_PI = 0.5 * Math.log(2.0 * Math.PI);

    
    /**
     * Default constructor.  Prohibit instantiation.
     */
    private Gamma() {
        super();
    }

    /**
     * Returns the natural logarithm of the gamma function &#915;(x).
     *
     * The implementation of this method is based on:
     * <ul>
     * <li><a href="http://mathworld.wolfram.com/GammaFunction.html">
     * Gamma Function</a>, equation (28).</li>
     * <li><a href="http://mathworld.wolfram.com/LanczosApproximation.html">
     * Lanczos Approximation</a>, equations (1) through (5).</li>
     * <li><a href="http://my.fit.edu/~gabdo/gamma.txt">Paul Godfrey, A note on
     * the computation of the convergent Lanczos complex Gamma approximation
     * </a></li>
     * </ul>
     * 
     * @param xx the value.
     * @return log(&#915;(x))
     */
    public static double logGamma(double xx) {
        double ret;

        if (Double.isNaN(xx) || (xx <= 0.0)) {
            ret = Double.NaN;
        } else {
            double g = 607.0 / 128.0;
            
            double sum = 0.0;
            for (int i = lanczos.length - 1; i > 0; --i) {
                sum = sum + (lanczos[i] / (xx + i));
            }
            sum = sum + lanczos[0];

            double tmp = xx + g + .5;
            ret = ((xx + .5) * Math.log(tmp)) - tmp +
                HALF_LOG_2_PI + Math.log(sum / xx);
        }

        return ret;
    }

}