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;
018
019import org.apache.commons.math3.exception.NotPositiveException;
020import org.apache.commons.math3.exception.NullArgumentException;
021import org.apache.commons.math3.exception.NumberIsTooLargeException;
022import org.apache.commons.math3.exception.MathIllegalArgumentException;
023import org.apache.commons.math3.exception.util.LocalizedFormats;
024import org.apache.commons.math3.util.MathArrays;
025
026/**
027 * Abstract base class for all implementations of the
028 * {@link UnivariateStatistic} interface.
029 * <p>
030 * Provides a default implementation of <code>evaluate(double[]),</code>
031 * delegating to <code>evaluate(double[], int, int)</code> in the natural way.
032 * </p>
033 * <p>
034 * Also includes a <code>test</code> method that performs generic parameter
035 * validation for the <code>evaluate</code> methods.</p>
036 *
037 */
038public abstract class AbstractUnivariateStatistic
039    implements UnivariateStatistic {
040
041    /** Stored data. */
042    private double[] storedData;
043
044    /**
045     * Set the data array.
046     * <p>
047     * The stored value is a copy of the parameter array, not the array itself.
048     * </p>
049     * @param values data array to store (may be null to remove stored data)
050     * @see #evaluate()
051     */
052    public void setData(final double[] values) {
053        storedData = (values == null) ? null : values.clone();
054    }
055
056    /**
057     * Get a copy of the stored data array.
058     * @return copy of the stored data array (may be null)
059     */
060    public double[] getData() {
061        return (storedData == null) ? null : storedData.clone();
062    }
063
064    /**
065     * Get a reference to the stored data array.
066     * @return reference to the stored data array (may be null)
067     */
068    protected double[] getDataRef() {
069        return storedData;
070    }
071
072    /**
073     * Set the data array.  The input array is copied, not referenced.
074     *
075     * @param values data array to store
076     * @param begin the index of the first element to include
077     * @param length the number of elements to include
078     * @throws MathIllegalArgumentException if values is null or the indices
079     * are not valid
080     * @see #evaluate()
081     */
082    public void setData(final double[] values, final int begin, final int length)
083    throws MathIllegalArgumentException {
084        if (values == null) {
085            throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
086        }
087
088        if (begin < 0) {
089            throw new NotPositiveException(LocalizedFormats.START_POSITION, begin);
090        }
091
092        if (length < 0) {
093            throw new NotPositiveException(LocalizedFormats.LENGTH, length);
094        }
095
096        if (begin + length > values.length) {
097            throw new NumberIsTooLargeException(LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END,
098                                                begin + length, values.length, true);
099        }
100        storedData = new double[length];
101        System.arraycopy(values, begin, storedData, 0, length);
102    }
103
104    /**
105     * Returns the result of evaluating the statistic over the stored data.
106     * <p>
107     * The stored array is the one which was set by previous calls to {@link #setData(double[])}.
108     * </p>
109     * @return the value of the statistic applied to the stored data
110     * @throws MathIllegalArgumentException if the stored data array is null
111     */
112    public double evaluate() throws MathIllegalArgumentException {
113        return evaluate(storedData);
114    }
115
116    /**
117     * {@inheritDoc}
118     */
119    public double evaluate(final double[] values) throws MathIllegalArgumentException {
120        test(values, 0, 0);
121        return evaluate(values, 0, values.length);
122    }
123
124    /**
125     * {@inheritDoc}
126     */
127    public abstract double evaluate(final double[] values, final int begin, final int length)
128    throws MathIllegalArgumentException;
129
130    /**
131     * {@inheritDoc}
132     */
133    public abstract UnivariateStatistic copy();
134
135    /**
136     * This method is used by <code>evaluate(double[], int, int)</code> methods
137     * to verify that the input parameters designate a subarray of positive length.
138     * <p>
139     * <ul>
140     * <li>returns <code>true</code> iff the parameters designate a subarray of
141     * positive length</li>
142     * <li>throws <code>MathIllegalArgumentException</code> if the array is null or
143     * or the indices are invalid</li>
144     * <li>returns <code>false</li> if the array is non-null, but
145     * <code>length</code> is 0.
146     * </ul></p>
147     *
148     * @param values the input array
149     * @param begin index of the first array element to include
150     * @param length the number of elements to include
151     * @return true if the parameters are valid and designate a subarray of positive length
152     * @throws MathIllegalArgumentException if the indices are invalid or the array is null
153     */
154    protected boolean test(
155        final double[] values,
156        final int begin,
157        final int length) throws MathIllegalArgumentException {
158        return MathArrays.verifyValues(values, begin, length, false);
159    }
160
161    /**
162     * This method is used by <code>evaluate(double[], int, int)</code> methods
163     * to verify that the input parameters designate a subarray of positive length.
164     * <p>
165     * <ul>
166     * <li>returns <code>true</code> iff the parameters designate a subarray of
167     * non-negative length</li>
168     * <li>throws <code>IllegalArgumentException</code> if the array is null or
169     * or the indices are invalid</li>
170     * <li>returns <code>false</li> if the array is non-null, but
171     * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>
172     * </ul></p>
173     *
174     * @param values the input array
175     * @param begin index of the first array element to include
176     * @param length the number of elements to include
177     * @param allowEmpty if <code>true</code> then zero length arrays are allowed
178     * @return true if the parameters are valid
179     * @throws MathIllegalArgumentException if the indices are invalid or the array is null
180     * @since 3.0
181     */
182    protected boolean test(final double[] values, final int begin,
183            final int length, final boolean allowEmpty) throws MathIllegalArgumentException {
184        return MathArrays.verifyValues(values, begin, length, allowEmpty);
185    }
186
187    /**
188     * This method is used by <code>evaluate(double[], double[], int, int)</code> methods
189     * to verify that the begin and length parameters designate a subarray of positive length
190     * and the weights are all non-negative, non-NaN, finite, and not all zero.
191     * <p>
192     * <ul>
193     * <li>returns <code>true</code> iff the parameters designate a subarray of
194     * positive length and the weights array contains legitimate values.</li>
195     * <li>throws <code>IllegalArgumentException</code> if any of the following are true:
196     * <ul><li>the values array is null</li>
197     *     <li>the weights array is null</li>
198     *     <li>the weights array does not have the same length as the values array</li>
199     *     <li>the weights array contains one or more infinite values</li>
200     *     <li>the weights array contains one or more NaN values</li>
201     *     <li>the weights array contains negative values</li>
202     *     <li>the start and length arguments do not determine a valid array</li></ul>
203     * </li>
204     * <li>returns <code>false</li> if the array is non-null, but
205     * <code>length</code> is 0.
206     * </ul></p>
207     *
208     * @param values the input array
209     * @param weights the weights array
210     * @param begin index of the first array element to include
211     * @param length the number of elements to include
212     * @return true if the parameters are valid and designate a subarray of positive length
213     * @throws MathIllegalArgumentException if the indices are invalid or the array is null
214     * @since 2.1
215     */
216    protected boolean test(
217        final double[] values,
218        final double[] weights,
219        final int begin,
220        final int length) throws MathIllegalArgumentException {
221        return MathArrays.verifyValues(values, weights, begin, length, false);
222    }
223
224    /**
225     * This method is used by <code>evaluate(double[], double[], int, int)</code> methods
226     * to verify that the begin and length parameters designate a subarray of positive length
227     * and the weights are all non-negative, non-NaN, finite, and not all zero.
228     * <p>
229     * <ul>
230     * <li>returns <code>true</code> iff the parameters designate a subarray of
231     * non-negative length and the weights array contains legitimate values.</li>
232     * <li>throws <code>MathIllegalArgumentException</code> if any of the following are true:
233     * <ul><li>the values array is null</li>
234     *     <li>the weights array is null</li>
235     *     <li>the weights array does not have the same length as the values array</li>
236     *     <li>the weights array contains one or more infinite values</li>
237     *     <li>the weights array contains one or more NaN values</li>
238     *     <li>the weights array contains negative values</li>
239     *     <li>the start and length arguments do not determine a valid array</li></ul>
240     * </li>
241     * <li>returns <code>false</li> if the array is non-null, but
242     * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>.
243     * </ul></p>
244     *
245     * @param values the input array.
246     * @param weights the weights array.
247     * @param begin index of the first array element to include.
248     * @param length the number of elements to include.
249     * @param allowEmpty if {@code true} than allow zero length arrays to pass.
250     * @return {@code true} if the parameters are valid.
251     * @throws NullArgumentException if either of the arrays are null
252     * @throws MathIllegalArgumentException if the array indices are not valid,
253     * the weights array contains NaN, infinite or negative elements, or there
254     * are no positive weights.
255     * @since 3.0
256     */
257    protected boolean test(final double[] values, final double[] weights,
258            final int begin, final int length, final boolean allowEmpty) throws MathIllegalArgumentException {
259
260        return MathArrays.verifyValues(values, weights, begin, length, allowEmpty);
261    }
262}
263