001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.math.distribution;
019    
020    import java.io.Serializable;
021    
022    /**
023     * Default implementation of
024     * {@link org.apache.commons.math.distribution.CauchyDistribution}.
025     *
026     * @since 1.1
027     * @version $Revision: 670469 $ $Date: 2008-06-23 10:01:38 +0200 (lun, 23 jun 2008) $
028     */
029    public class CauchyDistributionImpl extends AbstractContinuousDistribution 
030            implements CauchyDistribution, Serializable {
031        
032        /** Serializable version identifier */
033        private static final long serialVersionUID = 8589540077390120676L;
034    
035        /** The median of this distribution. */
036        private double median = 0;
037        
038        /** The scale of this distribution. */
039        private double scale = 1;
040        
041        /**
042         * Creates cauchy distribution with the medain equal to zero and scale
043         * equal to one. 
044         */
045        public CauchyDistributionImpl(){
046            this(0.0, 1.0);
047        }
048        
049        /**
050         * Create a cauchy distribution using the given median and scale.
051         * @param median median for this distribution
052         * @param s scale parameter for this distribution
053         */
054        public CauchyDistributionImpl(double median, double s){
055            super();
056            setMedian(median);
057            setScale(s);
058        }
059    
060        /**
061         * For this distribution, X, this method returns P(X &lt; <code>x</code>).
062         * @param x the value at which the CDF is evaluated.
063         * @return CDF evaluted at <code>x</code>. 
064         */
065        public double cumulativeProbability(double x) {
066            return 0.5 + (Math.atan((x - median) / scale) / Math.PI);
067        }
068        
069        /**
070         * Access the median.
071         * @return median for this distribution
072         */ 
073        public double getMedian() {
074            return median;
075        }
076    
077        /**
078         * Access the scale parameter.
079         * @return scale parameter for this distribution
080         */
081        public double getScale() {
082            return scale;
083        }
084        
085        /**
086         * For this distribution, X, this method returns the critical point x, such
087         * that P(X &lt; x) = <code>p</code>.
088         * <p>
089         * Returns <code>Double.NEGATIVE_INFINITY</code> for p=0 and 
090         * <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
091         *
092         * @param p the desired probability
093         * @return x, such that P(X &lt; x) = <code>p</code>
094         * @throws IllegalArgumentException if <code>p</code> is not a valid
095         *         probability.
096         */
097        public double inverseCumulativeProbability(double p) {
098            double ret;
099            if (p < 0.0 || p > 1.0) {
100                throw new IllegalArgumentException
101                    ("probability argument must be between 0 and 1 (inclusive)");
102            } else if (p == 0) {
103                ret = Double.NEGATIVE_INFINITY;
104            } else  if (p == 1) {
105                ret = Double.POSITIVE_INFINITY;
106            } else {
107                ret = median + scale * Math.tan(Math.PI * (p - .5));
108            }
109            return ret;
110        }
111        
112        /**
113         * Modify the median.
114         * @param median for this distribution
115         */
116        public void setMedian(double median) {
117            this.median = median;
118        }
119    
120        /**
121         * Modify the scale parameter.
122         * @param s scale parameter for this distribution
123         * @throws IllegalArgumentException if <code>sd</code> is not positive.
124         */
125        public void setScale(double s) {
126            if (s <= 0.0) {
127                throw new IllegalArgumentException(
128                    "Scale must be positive.");
129            }       
130            scale = s;
131        }
132        
133        /**
134         * Access the domain value lower bound, based on <code>p</code>, used to
135         * bracket a CDF root.  This method is used by
136         * {@link #inverseCumulativeProbability(double)} to find critical values.
137         * 
138         * @param p the desired probability for the critical value
139         * @return domain value lower bound, i.e.
140         *         P(X &lt; <i>lower bound</i>) &lt; <code>p</code> 
141         */
142        protected double getDomainLowerBound(double p) {
143            double ret;
144    
145            if (p < .5) {
146                ret = -Double.MAX_VALUE;
147            } else {
148                ret = getMedian();
149            }
150            
151            return ret;
152        }
153    
154        /**
155         * Access the domain value upper bound, based on <code>p</code>, used to
156         * bracket a CDF root.  This method is used by
157         * {@link #inverseCumulativeProbability(double)} to find critical values.
158         * 
159         * @param p the desired probability for the critical value
160         * @return domain value upper bound, i.e.
161         *         P(X &lt; <i>upper bound</i>) &gt; <code>p</code> 
162         */
163        protected double getDomainUpperBound(double p) {
164            double ret;
165    
166            if (p < .5) {
167                ret = getMedian();
168            } else {
169                ret = Double.MAX_VALUE;
170            }
171            
172            return ret;
173        }
174    
175        /**
176         * Access the initial domain value, based on <code>p</code>, used to
177         * bracket a CDF root.  This method is used by
178         * {@link #inverseCumulativeProbability(double)} to find critical values.
179         * 
180         * @param p the desired probability for the critical value
181         * @return initial domain value
182         */
183        protected double getInitialDomain(double p) {
184            double ret;
185    
186            if (p < .5) {
187                ret = getMedian() - getScale();
188            } else if (p > .5) {
189                ret = getMedian() + getScale();
190            } else {
191                ret = getMedian();
192            }
193            
194            return ret;
195        }
196    }