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