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 */
017package org.apache.commons.math3.stat.descriptive.summary;
018
019import java.io.Serializable;
020
021import org.apache.commons.math3.exception.MathIllegalArgumentException;
022import org.apache.commons.math3.exception.NullArgumentException;
023import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
024import org.apache.commons.math3.stat.descriptive.WeightedEvaluation;
025import org.apache.commons.math3.util.FastMath;
026import org.apache.commons.math3.util.MathUtils;
027
028/**
029 * Returns the product of the available values.
030 * <p>
031 * If there are no values in the dataset, then 1 is returned.
032 *  If any of the values are
033 * <code>NaN</code>, then <code>NaN</code> is returned.</p>
034 * <p>
035 * <strong>Note that this implementation is not synchronized.</strong> If
036 * multiple threads access an instance of this class concurrently, and at least
037 * one of the threads invokes the <code>increment()</code> or
038 * <code>clear()</code> method, it must be synchronized externally.</p>
039 *
040 */
041public class Product extends AbstractStorelessUnivariateStatistic implements Serializable, WeightedEvaluation {
042
043    /** Serializable version identifier */
044    private static final long serialVersionUID = 2824226005990582538L;
045
046    /**The number of values that have been added */
047    private long n;
048
049    /**
050     * The current Running Product.
051     */
052    private double value;
053
054    /**
055     * Create a Product instance
056     */
057    public Product() {
058        n = 0;
059        value = 1;
060    }
061
062    /**
063     * Copy constructor, creates a new {@code Product} identical
064     * to the {@code original}
065     *
066     * @param original the {@code Product} instance to copy
067     * @throws NullArgumentException  if original is null
068     */
069    public Product(Product original) throws NullArgumentException {
070        copy(original, this);
071    }
072
073    /**
074     * {@inheritDoc}
075     */
076    @Override
077    public void increment(final double d) {
078        value *= d;
079        n++;
080    }
081
082    /**
083     * {@inheritDoc}
084     */
085    @Override
086    public double getResult() {
087        return value;
088    }
089
090    /**
091     * {@inheritDoc}
092     */
093    public long getN() {
094        return n;
095    }
096
097    /**
098     * {@inheritDoc}
099     */
100    @Override
101    public void clear() {
102        value = 1;
103        n = 0;
104    }
105
106    /**
107     * Returns the product of the entries in the specified portion of
108     * the input array, or <code>Double.NaN</code> if the designated subarray
109     * is empty.
110     * <p>
111     * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
112     *
113     * @param values the input array
114     * @param begin index of the first array element to include
115     * @param length the number of elements to include
116     * @return the product of the values or 1 if length = 0
117     * @throws MathIllegalArgumentException if the array is null or the array index
118     *  parameters are not valid
119     */
120    @Override
121    public double evaluate(final double[] values, final int begin, final int length)
122    throws MathIllegalArgumentException {
123        double product = Double.NaN;
124        if (test(values, begin, length, true)) {
125            product = 1.0;
126            for (int i = begin; i < begin + length; i++) {
127                product *= values[i];
128            }
129        }
130        return product;
131    }
132
133    /**
134     * <p>Returns the weighted product of the entries in the specified portion of
135     * the input array, or <code>Double.NaN</code> if the designated subarray
136     * is empty.</p>
137     *
138     * <p>Throws <code>MathIllegalArgumentException</code> if any of the following are true:
139     * <ul><li>the values array is null</li>
140     *     <li>the weights array is null</li>
141     *     <li>the weights array does not have the same length as the values array</li>
142     *     <li>the weights array contains one or more infinite values</li>
143     *     <li>the weights array contains one or more NaN values</li>
144     *     <li>the weights array contains negative values</li>
145     *     <li>the start and length arguments do not determine a valid array</li>
146     * </ul></p>
147     *
148     * <p>Uses the formula, <pre>
149     *    weighted product = &prod;values[i]<sup>weights[i]</sup>
150     * </pre>
151     * that is, the weights are applied as exponents when computing the weighted product.</p>
152     *
153     * @param values the input array
154     * @param weights the weights array
155     * @param begin index of the first array element to include
156     * @param length the number of elements to include
157     * @return the product of the values or 1 if length = 0
158     * @throws MathIllegalArgumentException if the parameters are not valid
159     * @since 2.1
160     */
161    public double evaluate(final double[] values, final double[] weights,
162        final int begin, final int length) throws MathIllegalArgumentException {
163        double product = Double.NaN;
164        if (test(values, weights, begin, length, true)) {
165            product = 1.0;
166            for (int i = begin; i < begin + length; i++) {
167                product *= FastMath.pow(values[i], weights[i]);
168            }
169        }
170        return product;
171    }
172
173    /**
174     * <p>Returns the weighted product of the entries in the input array.</p>
175     *
176     * <p>Throws <code>MathIllegalArgumentException</code> if any of the following are true:
177     * <ul><li>the values array is null</li>
178     *     <li>the weights array is null</li>
179     *     <li>the weights array does not have the same length as the values array</li>
180     *     <li>the weights array contains one or more infinite values</li>
181     *     <li>the weights array contains one or more NaN values</li>
182     *     <li>the weights array contains negative values</li>
183     * </ul></p>
184     *
185     * <p>Uses the formula, <pre>
186     *    weighted product = &prod;values[i]<sup>weights[i]</sup>
187     * </pre>
188     * that is, the weights are applied as exponents when computing the weighted product.</p>
189     *
190     * @param values the input array
191     * @param weights the weights array
192     * @return the product of the values or Double.NaN if length = 0
193     * @throws MathIllegalArgumentException if the parameters are not valid
194     * @since 2.1
195     */
196    public double evaluate(final double[] values, final double[] weights)
197    throws MathIllegalArgumentException {
198        return evaluate(values, weights, 0, values.length);
199    }
200
201
202    /**
203     * {@inheritDoc}
204     */
205    @Override
206    public Product copy() {
207        Product result = new Product();
208        // No try-catch or advertised exception because args are valid
209        copy(this, result);
210        return result;
211    }
212
213    /**
214     * Copies source to dest.
215     * <p>Neither source nor dest can be null.</p>
216     *
217     * @param source Product to copy
218     * @param dest Product to copy to
219     * @throws NullArgumentException if either source or dest is null
220     */
221    public static void copy(Product source, Product dest)
222        throws NullArgumentException {
223        MathUtils.checkNotNull(source);
224        MathUtils.checkNotNull(dest);
225        dest.setData(source.getDataRef());
226        dest.n = source.n;
227        dest.value = source.value;
228    }
229
230}