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