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    package org.apache.commons.math.stat.descriptive.moment;
018    
019    import java.io.Serializable;
020    
021    import org.apache.commons.math.MathRuntimeException;
022    import org.apache.commons.math.exception.util.LocalizedFormats;
023    import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
024    import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic;
025    import org.apache.commons.math.stat.descriptive.summary.SumOfLogs;
026    import org.apache.commons.math.util.FastMath;
027    
028    /**
029     * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
030     * geometric mean </a> of the available values.
031     * <p>
032     * Uses a {@link SumOfLogs} instance to compute sum of logs and returns
033     * <code> exp( 1/n  (sum of logs) ).</code>  Therefore, </p>
034     * <ul>
035     * <li>If any of values are < 0, the result is <code>NaN.</code></li>
036     * <li>If all values are non-negative and less than
037     * <code>Double.POSITIVE_INFINITY</code>,  but at least one value is 0, the
038     * result is <code>0.</code></li>
039     * <li>If both <code>Double.POSITIVE_INFINITY</code> and
040     * <code>Double.NEGATIVE_INFINITY</code> are among the values, the result is
041     * <code>NaN.</code></li>
042     * </ul> </p>
043     * <p>
044     * <strong>Note that this implementation is not synchronized.</strong> If
045     * multiple threads access an instance of this class concurrently, and at least
046     * one of the threads invokes the <code>increment()</code> or
047     * <code>clear()</code> method, it must be synchronized externally.</p>
048     *
049     *
050     * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
051     */
052    public class GeometricMean extends AbstractStorelessUnivariateStatistic implements Serializable {
053    
054        /** Serializable version identifier */
055        private static final long serialVersionUID = -8178734905303459453L;
056    
057        /** Wrapped SumOfLogs instance */
058        private StorelessUnivariateStatistic sumOfLogs;
059    
060        /**
061         * Create a GeometricMean instance
062         */
063        public GeometricMean() {
064            sumOfLogs = new SumOfLogs();
065        }
066    
067        /**
068         * Copy constructor, creates a new {@code GeometricMean} identical
069         * to the {@code original}
070         *
071         * @param original the {@code GeometricMean} instance to copy
072         */
073        public GeometricMean(GeometricMean original) {
074            super();
075            copy(original, this);
076        }
077    
078        /**
079         * Create a GeometricMean instance using the given SumOfLogs instance
080         * @param sumOfLogs sum of logs instance to use for computation
081         */
082        public GeometricMean(SumOfLogs sumOfLogs) {
083            this.sumOfLogs = sumOfLogs;
084        }
085    
086        /**
087         * {@inheritDoc}
088         */
089        @Override
090        public GeometricMean copy() {
091            GeometricMean result = new GeometricMean();
092            copy(this, result);
093            return result;
094        }
095    
096        /**
097         * {@inheritDoc}
098         */
099        @Override
100        public void increment(final double d) {
101            sumOfLogs.increment(d);
102        }
103    
104        /**
105         * {@inheritDoc}
106         */
107        @Override
108        public double getResult() {
109            if (sumOfLogs.getN() > 0) {
110                return FastMath.exp(sumOfLogs.getResult() / sumOfLogs.getN());
111            } else {
112                return Double.NaN;
113            }
114        }
115    
116        /**
117         * {@inheritDoc}
118         */
119        @Override
120        public void clear() {
121            sumOfLogs.clear();
122        }
123    
124        /**
125         * Returns the geometric mean of the entries in the specified portion
126         * of the input array.
127         * <p>
128         * See {@link GeometricMean} for details on the computing algorithm.</p>
129         * <p>
130         * Throws <code>IllegalArgumentException</code> if the array is null.</p>
131         *
132         * @param values input array containing the values
133         * @param begin first array element to include
134         * @param length the number of elements to include
135         * @return the geometric mean or Double.NaN if length = 0 or
136         * any of the values are &lt;= 0.
137         * @throws IllegalArgumentException if the input array is null or the array
138         * index parameters are not valid
139         */
140        @Override
141        public double evaluate(
142            final double[] values, final int begin, final int length) {
143            return FastMath.exp(
144                sumOfLogs.evaluate(values, begin, length) / length);
145        }
146    
147        /**
148         * {@inheritDoc}
149         */
150        public long getN() {
151            return sumOfLogs.getN();
152        }
153    
154        /**
155         * <p>Sets the implementation for the sum of logs.</p>
156         * <p>This method must be activated before any data has been added - i.e.,
157         * before {@link #increment(double) increment} has been used to add data;
158         * otherwise an IllegalStateException will be thrown.</p>
159         *
160         * @param sumLogImpl the StorelessUnivariateStatistic instance to use
161         * for computing the log sum
162         * @throws IllegalStateException if data has already been added
163         *  (i.e if n > 0)
164         */
165        public void setSumLogImpl(
166                StorelessUnivariateStatistic sumLogImpl) {
167            checkEmpty();
168            this.sumOfLogs = sumLogImpl;
169        }
170    
171        /**
172         * Returns the currently configured sum of logs implementation
173         *
174         * @return the StorelessUnivariateStatistic implementing the log sum
175         */
176        public StorelessUnivariateStatistic getSumLogImpl() {
177            return sumOfLogs;
178        }
179    
180        /**
181         * Copies source to dest.
182         * <p>Neither source nor dest can be null.</p>
183         *
184         * @param source GeometricMean to copy
185         * @param dest GeometricMean to copy to
186         * @throws NullPointerException if either source or dest is null
187         */
188        public static void copy(GeometricMean source, GeometricMean dest) {
189            dest.setData(source.getDataRef());
190            dest.sumOfLogs = source.sumOfLogs.copy();
191        }
192    
193    
194        /**
195         * Throws IllegalStateException if n > 0.
196         */
197        private void checkEmpty() {
198            if (getN() > 0) {
199                throw MathRuntimeException.createIllegalStateException(
200                        LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC,
201                        getN());
202            }
203        }
204    
205    }