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     */
017    package org.apache.commons.math.stat.descriptive;
018    
019    import org.apache.commons.math.exception.DimensionMismatchException;
020    import org.apache.commons.math.exception.NotPositiveException;
021    import org.apache.commons.math.exception.NullArgumentException;
022    import org.apache.commons.math.exception.NumberIsTooLargeException;
023    import org.apache.commons.math.exception.MathIllegalArgumentException;
024    import org.apache.commons.math.exception.util.LocalizedFormats;
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 1131229 2011-06-03 20:49:25Z luc $
038     */
039    public 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.
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         * @see #evaluate()
079         */
080        public void setData(final double[] values, final int begin, final int length) {
081            storedData = new double[length];
082            System.arraycopy(values, begin, storedData, 0, length);
083        }
084    
085        /**
086         * Returns the result of evaluating the statistic over the stored data.
087         * <p>
088         * The stored array is the one which was set by previous calls to
089         * </p>
090         * @return the value of the statistic applied to the stored data
091         */
092        public double evaluate() {
093            return evaluate(storedData);
094        }
095    
096        /**
097         * {@inheritDoc}
098         */
099        public double evaluate(final double[] values) {
100            test(values, 0, 0);
101            return evaluate(values, 0, values.length);
102        }
103    
104        /**
105         * {@inheritDoc}
106         */
107        public abstract double evaluate(final double[] values, final int begin, final int length);
108    
109        /**
110         * {@inheritDoc}
111         */
112        public abstract UnivariateStatistic copy();
113    
114        /**
115         * This method is used by <code>evaluate(double[], int, int)</code> methods
116         * to verify that the input parameters designate a subarray of positive length.
117         * <p>
118         * <ul>
119         * <li>returns <code>true</code> iff the parameters designate a subarray of
120         * positive length</li>
121         * <li>throws <code>IllegalArgumentException</code> if the array is null or
122         * or the indices are invalid</li>
123         * <li>returns <code>false</li> if the array is non-null, but
124         * <code>length</code> is 0.
125         * </ul></p>
126         *
127         * @param values the input array
128         * @param begin index of the first array element to include
129         * @param length the number of elements to include
130         * @return true if the parameters are valid and designate a subarray of positive length
131         * @throws IllegalArgumentException if the indices are invalid or the array is null
132         */
133        protected boolean test(
134            final double[] values,
135            final int begin,
136            final int length) {
137            return test(values, begin, length, false);
138        }
139    
140        /**
141         * This method is used by <code>evaluate(double[], int, int)</code> methods
142         * to verify that the input parameters designate a subarray of positive length.
143         * <p>
144         * <ul>
145         * <li>returns <code>true</code> iff the parameters designate a subarray of
146         * non-negative length</li>
147         * <li>throws <code>IllegalArgumentException</code> if the array is null or
148         * or the indices are invalid</li>
149         * <li>returns <code>false</li> if the array is non-null, but
150         * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>
151         * </ul></p>
152         *
153         * @param values the input array
154         * @param begin index of the first array element to include
155         * @param length the number of elements to include
156         * @param allowEmpty if <code>true</code> then zero length arrays are allowed
157         * @return true if the parameters are valid
158         * @throws IllegalArgumentException if the indices are invalid or the array is null
159         * @since 3.0
160         */
161        protected boolean test(final double[] values, final int begin, final int length, final boolean allowEmpty){
162    
163            if (values == null) {
164                throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
165            }
166    
167            if (begin < 0) {
168                throw new NotPositiveException(LocalizedFormats.START_POSITION, begin);
169            }
170    
171            if (length < 0) {
172                throw new NotPositiveException(LocalizedFormats.LENGTH, length);
173            }
174    
175            if (begin + length > values.length) {
176                throw new NumberIsTooLargeException(LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END,
177                                                    begin + length, values.length, true);
178            }
179    
180            if (length == 0 && !allowEmpty) {
181                return false;
182            }
183    
184            return true;
185    
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 IllegalArgumentException 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) {
222            return test(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>IllegalArgumentException</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 IllegalArgumentException if the indices are invalid or the array
253         * is {@code null}.
254         * @since 3.0
255         */
256        protected boolean test(final double[] values, final double[] weights, final int begin, final int length, final boolean allowEmpty){
257    
258            if (weights == null) {
259                throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
260            }
261    
262            if (weights.length != values.length) {
263                throw new DimensionMismatchException(weights.length, values.length);
264            }
265    
266            boolean containsPositiveWeight = false;
267            for (int i = begin; i < begin + length; i++) {
268                if (Double.isNaN(weights[i])) {
269                    throw new MathIllegalArgumentException(LocalizedFormats.NAN_ELEMENT_AT_INDEX, i);
270                }
271                if (Double.isInfinite(weights[i])) {
272                    throw new MathIllegalArgumentException(LocalizedFormats.INFINITE_ARRAY_ELEMENT, weights[i], i);
273                }
274                if (weights[i] < 0) {
275                    throw new MathIllegalArgumentException(LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX, i, weights[i]);
276                }
277                if (!containsPositiveWeight && weights[i] > 0.0) {
278                    containsPositiveWeight = true;
279                }
280            }
281    
282            if (!containsPositiveWeight) {
283                throw new MathIllegalArgumentException(LocalizedFormats.WEIGHT_AT_LEAST_ONE_NON_ZERO);
284            }
285    
286            return test(values, begin, length, allowEmpty);
287        }
288    }
289