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    import org.apache.commons.math.MathRuntimeException;
023    
024    /**
025     * Default implementation of
026     * {@link org.apache.commons.math.distribution.CauchyDistribution}.
027     *
028     * @since 1.1
029     * @version $Revision: 772119 $ $Date: 2009-05-06 05:43:28 -0400 (Wed, 06 May 2009) $
030     */
031    public class CauchyDistributionImpl extends AbstractContinuousDistribution 
032            implements CauchyDistribution, Serializable {
033        
034        /** Serializable version identifier */
035        private static final long serialVersionUID = 8589540077390120676L;
036    
037        /** The median of this distribution. */
038        private double median = 0;
039        
040        /** The scale of this distribution. */
041        private double scale = 1;
042        
043        /**
044         * Creates cauchy distribution with the medain equal to zero and scale
045         * equal to one. 
046         */
047        public CauchyDistributionImpl(){
048            this(0.0, 1.0);
049        }
050        
051        /**
052         * Create a cauchy distribution using the given median and scale.
053         * @param median median for this distribution
054         * @param s scale parameter for this distribution
055         */
056        public CauchyDistributionImpl(double median, double s){
057            super();
058            setMedian(median);
059            setScale(s);
060        }
061    
062        /**
063         * For this distribution, X, this method returns P(X &lt; <code>x</code>).
064         * @param x the value at which the CDF is evaluated.
065         * @return CDF evaluted at <code>x</code>. 
066         */
067        public double cumulativeProbability(double x) {
068            return 0.5 + (Math.atan((x - median) / scale) / Math.PI);
069        }
070        
071        /**
072         * Access the median.
073         * @return median for this distribution
074         */ 
075        public double getMedian() {
076            return median;
077        }
078    
079        /**
080         * Access the scale parameter.
081         * @return scale parameter for this distribution
082         */
083        public double getScale() {
084            return scale;
085        }
086        
087        /**
088         * For this distribution, X, this method returns the critical point x, such
089         * that P(X &lt; x) = <code>p</code>.
090         * <p>
091         * Returns <code>Double.NEGATIVE_INFINITY</code> for p=0 and 
092         * <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
093         *
094         * @param p the desired probability
095         * @return x, such that P(X &lt; x) = <code>p</code>
096         * @throws IllegalArgumentException if <code>p</code> is not a valid
097         *         probability.
098         */
099        @Override
100        public double inverseCumulativeProbability(double p) {
101            double ret;
102            if (p < 0.0 || p > 1.0) {
103                throw MathRuntimeException.createIllegalArgumentException(
104                      "{0} out of [{1}, {2}] range", p, 0.0, 1.0);
105            } else if (p == 0) {
106                ret = Double.NEGATIVE_INFINITY;
107            } else  if (p == 1) {
108                ret = Double.POSITIVE_INFINITY;
109            } else {
110                ret = median + scale * Math.tan(Math.PI * (p - .5));
111            }
112            return ret;
113        }
114        
115        /**
116         * Modify the median.
117         * @param median for this distribution
118         */
119        public void setMedian(double median) {
120            this.median = median;
121        }
122    
123        /**
124         * Modify the scale parameter.
125         * @param s scale parameter for this distribution
126         * @throws IllegalArgumentException if <code>sd</code> is not positive.
127         */
128        public void setScale(double s) {
129            if (s <= 0.0) {
130                throw MathRuntimeException.createIllegalArgumentException(
131                      "scale must be positive ({0})", s);
132            }       
133            scale = s;
134        }
135        
136        /**
137         * Access the domain value lower bound, based on <code>p</code>, used to
138         * bracket a CDF root.  This method is used by
139         * {@link #inverseCumulativeProbability(double)} to find critical values.
140         * 
141         * @param p the desired probability for the critical value
142         * @return domain value lower bound, i.e.
143         *         P(X &lt; <i>lower bound</i>) &lt; <code>p</code> 
144         */
145        @Override
146        protected double getDomainLowerBound(double p) {
147            double ret;
148    
149            if (p < .5) {
150                ret = -Double.MAX_VALUE;
151            } else {
152                ret = getMedian();
153            }
154            
155            return ret;
156        }
157    
158        /**
159         * Access the domain value upper bound, based on <code>p</code>, used to
160         * bracket a CDF root.  This method is used by
161         * {@link #inverseCumulativeProbability(double)} to find critical values.
162         * 
163         * @param p the desired probability for the critical value
164         * @return domain value upper bound, i.e.
165         *         P(X &lt; <i>upper bound</i>) &gt; <code>p</code> 
166         */
167        @Override
168        protected double getDomainUpperBound(double p) {
169            double ret;
170    
171            if (p < .5) {
172                ret = getMedian();
173            } else {
174                ret = Double.MAX_VALUE;
175            }
176            
177            return ret;
178        }
179    
180        /**
181         * Access the initial domain value, based on <code>p</code>, used to
182         * bracket a CDF root.  This method is used by
183         * {@link #inverseCumulativeProbability(double)} to find critical values.
184         * 
185         * @param p the desired probability for the critical value
186         * @return initial domain value
187         */
188        @Override
189        protected double getInitialDomain(double p) {
190            double ret;
191    
192            if (p < .5) {
193                ret = getMedian() - getScale();
194            } else if (p > .5) {
195                ret = getMedian() + getScale();
196            } else {
197                ret = getMedian();
198            }
199            
200            return ret;
201        }
202    }