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