/*
 Copyright (C) 2008 Richard Gomes

 This source code is release under the BSD License.

 This file is part of JQuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://jquantlib.org/

 JQuantLib is free software: you can redistribute it and/or modify it
 under the terms of the JQuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 . The license is also available online at
 .

 This program 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 license for more details.

 JQuantLib is based on QuantLib. http://quantlib.org/
 When applicable, the original copyright notice follows this notice.
 */

package org.jquantlib.math.distributions;

import org.jquantlib.QL;
import org.jquantlib.Settings;
import org.jquantlib.lang.exceptions.LibraryException;
import org.jquantlib.math.Closeness;
import org.jquantlib.math.Constants;
import org.jquantlib.math.randomnumbers.InverseCumulative;


/**
 * The inverse normal cumulative distribution is a non-linear function for which
 * no closed-form solution exists. The function is continuous, monotonically increasing,
 * infinitely differentiable, and maps the open interval (0,1) to the whole real line.
 *
 * @see 
 * 		An algorithm for computing the inverse normal cumulative distribution function
 *
 * @author Dominik Holenstein
 */

public class InverseCumulativeNormal implements InverseCumulative {

    static final private String SIGMA_MUST_BE_POSITIVE = "sigma must be greater than 0.0";

    //
    // static final private fields
    //

    static final private double a1 = -3.969683028665376e+01;
    static final private double a2 =  2.209460984245205e+02;
    static final private double a3 = -2.759285104469687e+02;
    static final private double a4 =  1.383577518672690e+02;
    static final private double a5 = -3.066479806614716e+01;
    static final private double a6 =  2.506628277459239e+00;

    static final private double b1 = -5.447609879822406e+01;
    static final private double b2 =  1.615858368580409e+02;
    static final private double b3 = -1.556989798598866e+02;
    static final private double b4 =  6.680131188771972e+01;
    static final private double b5 = -1.328068155288572e+01;

    static final private double c1 = -7.784894002430293e-03;
    static final private double c2 = -3.223964580411365e-01;
    static final private double c3 = -2.400758277161838e+00;
    static final private double c4 = -2.549732539343734e+00;
    static final private double c5 =  4.374664141464968e+00;
    static final private double c6 =  2.938163982698783e+00;

    static final private double d1 =  7.784695709041462e-03;
    static final private double d2 =  3.224671290700398e-01;
    static final private double d3 =  2.445134137142996e+00;
    static final private double d4 =  3.754408661907416e+00;

    //
    // Limits of the approximation regions (break-points)
    //
    static final private double xlow = 0.02425;
    static final private double xhigh = 1.0 - xlow;


    //
    // protected fields
    //

    protected double average;
    protected double sigma;


    //
    // private fields
    //

    // refinement for higher precision
    private final boolean highPrecision;


    //
    // public constructors
    //

    public InverseCumulativeNormal() {
        this(0.0);
    }

    public InverseCumulativeNormal(final double average) {
        this(average, 1.0);
    }

    public InverseCumulativeNormal(final double average, final double sigma) {
        QL.require(sigma > 0.0 , SIGMA_MUST_BE_POSITIVE); // TODO: message
        this.average = average;
        this.sigma = sigma;
        this.highPrecision = new Settings().isRefineHighPrecisionUsingHalleysMethod();
    }


    //
    // implements UnaryFunction
    //

    /**
     * Computes the inverse cumulative normal distribution.
     * @param x
     * @returns average + z * sigma
     */
    @Override
    public double op(double x)/* @ReadOnly */{
        QL.require(sigma > 0.0 , SIGMA_MUST_BE_POSITIVE); // TODO: message

        double z;
        double r;

        if (x < 0.0 || x > 1.0) {
            // try to recover if due to numerical error
            if (Closeness.isCloseEnough(x, 1.0)) {
                x = 1.0;
            } else if (Math.abs(x) < Constants.QL_EPSILON) {
                x = 0.0;
            } else {
                throw new LibraryException(SIGMA_MUST_BE_POSITIVE); // QA:[RG]::verified
            }
        }

        if (x < xlow) {
            // Rational approximation for the lower region 0 

Ads help maintain this website.