/*
 * LogNormalDistribution.java
 *
 * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 *  BEAST 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.math.distributions;

import dr.math.UnivariateFunction;

/**
 * log normal distribution (pdf, cdf, quantile)
 *
 * @author Korbinian Strimmer
 * @version $Id: LogNormalDistribution.java,v 1.3 2005/06/21 16:25:15 beth Exp $
 */
public class LogNormalDistribution implements Distribution {
    //
    // Public stuff
    //

    /**
     * @param M the mean of the random variable's natural logarithm
     * @param S the stdev of the random variable's natural logarithm
     */
    public LogNormalDistribution(double M, double S) {
        this.M = M;
        this.S = S;
    }

    public final double getM() {
        return M;
    }

    public final void setM(double M) {
        this.M = M;
    }

    public final double getS() {
        return S;
    }

    public final void setS(double S) {
        this.S = S;
    }

    public double pdf(double x) {
        return pdf(x, M, S);
    }

    public double logPdf(double x) {
        return logPdf(x, M, S);
    }

    public double cdf(double x) {
        return cdf(x, M, S);
    }

    public double quantile(double y) {
        return quantile(y, M, S);
    }

    public double mean() {
        return mean(M, S);
    }

    public double variance() {
        return variance(M, S);
    }

    public double mode() {
        return mode(M, S);
    }

    public final UnivariateFunction getProbabilityDensityFunction() {
        return pdfFunction;
    }

    private final UnivariateFunction pdfFunction = new UnivariateFunction() {
        public final double evaluate(double x) {
            return pdf(x);
        }

        public final double getLowerBound() {
            return 0.0;
        }

        public final double getUpperBound() {
            return Double.POSITIVE_INFINITY;
        }
    };

    /**
     * probability density function
     *
     * @param x argument
     * @param M log mean
     * @param S log standard deviation
     * @return pdf at x
     */
    public static double pdf(double x, double M, double S) {
        if (x <= 0) return 0; // no density for x<=0

        return NormalDistribution.pdf(Math.log(x), M, S) / x;
    }

    /**
     * the natural log of the probability density function of the distribution
     *
     * @param x argument
     * @param M log mean
     * @param S log standard deviation
     * @return log pdf at x
     */
    public static double logPdf(double x, double M, double S) {
        if (x < 0) return Double.NEGATIVE_INFINITY; // no density for x<0

        return NormalDistribution.logPdf(Math.log(x), M, S) - Math.log(x);
    }

    /**
     * cumulative density function
     *
     * @param x argument
     * @param M log mean
     * @param S log standard deviation
     * @return cdf at x
     */
    public static double cdf(double x, double M, double S) {
        if (x < 0) return 0; // no density for x<0

        return NormalDistribution.cdf(Math.log(x), M, S, false);
        //return NormalDistribution.cdf(Math.log(x), M, S);
    }

    /**
     * quantiles (=inverse cumulative density function)
     *
     * @param z argument
     * @param M log mean
     * @param S log standard deviation
     * @return icdf at z
     */
    public static double quantile(double z, double M, double S) {
        return Math.exp(NormalDistribution.quantile(z, M, S));
    }

    /**
     * mean
     *
     * @param M log mean
     * @param S log standard deviation
     * @return mean
     */
    public static double mean(double M, double S) {
        return Math.exp(M + (S * S / 2));
    }

    /**
     * mode
     *
     * @param M log mean
     * @param S log standard deviation
     * @return mode
     */
    public static double mode(double M, double S) {
        return Math.exp(M - S * S);
    }

    /**
     * variance
     *
     * @param M log mean
     * @param S log standard deviation
     * @return variance
     */
    public static double variance(double M, double S) {
        final double S2 = S * S;

        return Math.exp(S2 + 2 * M) * (Math.exp(S2) - 1);
    }

    // Private

    protected double M, S;
}
