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