/*
 * KernelDensityEstimatorDistribution.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 com.sun.tools.doclets.formats.html.NestedClassWriterImpl;
import dr.math.UnivariateFunction;
import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.MaxIterationsExceededException;
import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction;
import org.apache.commons.math.analysis.UnivariateRealFunction;
import org.apache.commons.math.analysis.integration.SimpsonIntegrator;
import org.apache.commons.math.analysis.solvers.NewtonSolver;

/**
 * @author Marc Suchard
 */
public abstract class KernelDensityEstimatorDistribution implements Distribution {

    KernelDensityEstimatorDistribution(Double[] sample, Double lowerBound, Double upperBound, Double bandWidth) {

        this.sample = new double[sample.length];
        for (int i = 0; i < sample.length; i++) {
            this.sample[i] = sample[i];
        }
        this.N = sample.length;
        processBounds(lowerBound, upperBound);
        setBandWidth(bandWidth);
    }

    abstract protected double evaluateKernel(double x);

    abstract protected void processBounds(Double lowerBound, Double upperBound);

    abstract protected void setBandWidth(Double bandWidth);

    abstract public double getFromPoint();

    abstract public double getToPoint();

    /**
     * probability density function of the distribution
     *
     * @param x argument
     * @return pdf value
     */
    public double pdf(double x) {
        return evaluateKernel(x);
    }

    /**
     * the natural log of the probability density function of the distribution
     *
     * @param x argument
     * @return log pdf value
     */
    public double logPdf(double x) {
        return Math.log(pdf(x));
    }

    /**
     * cumulative density function of the distribution
     *
     * @param x argument
     * @return cdf value
     */
    public double cdf(double x) {

        double cdf;
        try {
            cdf = cdfFunction.value(x);
        } catch (FunctionEvaluationException e) {
            throw new RuntimeException(e.getMessage());
        }

        return cdf;
    }

    /**
     * quantile (inverse cumulative density function) of the distribution
     *
     * @param y argument
     * @return cdf value
     */
    public double quantile(final double y) {

        final DifferentiableUnivariateRealFunction root =
                new DifferentiableUnivariateRealFunction() {

            @Override
            public UnivariateRealFunction derivative() {
                return pdfFunction;
            }

            @Override
            public double value(double x) throws FunctionEvaluationException {

                return cdfFunction.value(x) - y;
            }
        };

        NewtonSolver solver = new NewtonSolver(root);

        double q;
        try {
            q = solver.solve(getFromPoint(), getToPoint());
        } catch (MaxIterationsExceededException e) {
            throw new RuntimeException(e.getMessage());
        } catch (FunctionEvaluationException e) {
            throw new RuntimeException(e.getMessage());
        }

        return q;
    }

    /**
     * mean of the distribution
     *
     * @return mean
     */
    public double mean() {
        throw new RuntimeException("Not Implemented.");
    }

    /**
     * variance of the distribution
     *
     * @return variance
     */
    public double variance() {
        throw new RuntimeException("Not Implemented.");
    }

    /**
     * @return a probability density function representing this distribution
     */
    public UnivariateFunction getProbabilityDensityFunction() {
        throw new RuntimeException("Not Implemented.");
    }

    public double getBandWidth() {
        return bandWidth;
    }

    public enum Type {
        GAUSSIAN("Gaussian"),
        GAMMA("Gamma"),
        LOG_TRANSFORMED_GAUSSIAN("LogTransformedGaussian"),
        BETA("Beta");

        Type(String text) {
            this.text = text;
        }

        public String getText() {
            return text;
        }

        public static Type parseFromString(String text) {
            for (Type format : Type.values()) {
                if (format.getText().compareToIgnoreCase(text) == 0)
                    return format;
            }
            return null;
        }

        private final String text;
    }

    protected int N;
    protected double lowerBound;
    protected double upperBound;
    double bandWidth;
    protected double[] sample;

    private final SimpsonIntegrator integrator = new SimpsonIntegrator();

    private final UnivariateRealFunction pdfFunction = new UnivariateRealFunction() {

        @Override
        public double value(double x) throws FunctionEvaluationException {
            return pdf(x);
        }
    };

    private final UnivariateRealFunction cdfFunction = new UnivariateRealFunction() {

        @Override
        public double value(double x) throws FunctionEvaluationException {
            double rangeMin = getFromPoint();

            double cdf;
            try {
                cdf = integrator.integrate(pdfFunction, rangeMin, x);
            } catch (MaxIterationsExceededException e) {
                throw new RuntimeException(e.getMessage());
            }

            return cdf;
        }
    };
}

@@ -1042,73 +1042,8 @@
 s;%0A%0A
-import com.sun.tools.doclets.formats.html.NestedClassWriterImpl;%0A
 impo

remove missing import; error in java 9