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.math3.stat.descriptive;
018
019 import org.apache.commons.math3.exception.DimensionMismatchException;
020 import org.apache.commons.math3.exception.NotPositiveException;
021 import org.apache.commons.math3.exception.NullArgumentException;
022 import org.apache.commons.math3.exception.NumberIsTooLargeException;
023 import org.apache.commons.math3.exception.MathIllegalArgumentException;
024 import org.apache.commons.math3.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 1416643 2012-12-03 19:37:14Z tn $
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. 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 test(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
186 if (values == null) {
187 throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
188 }
189
190 if (begin < 0) {
191 throw new NotPositiveException(LocalizedFormats.START_POSITION, begin);
192 }
193
194 if (length < 0) {
195 throw new NotPositiveException(LocalizedFormats.LENGTH, length);
196 }
197
198 if (begin + length > values.length) {
199 throw new NumberIsTooLargeException(LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END,
200 begin + length, values.length, true);
201 }
202
203 if (length == 0 && !allowEmpty) {
204 return false;
205 }
206
207 return true;
208
209 }
210
211 /**
212 * This method is used by <code>evaluate(double[], double[], int, int)</code> methods
213 * to verify that the begin and length parameters designate a subarray of positive length
214 * and the weights are all non-negative, non-NaN, finite, and not all zero.
215 * <p>
216 * <ul>
217 * <li>returns <code>true</code> iff the parameters designate a subarray of
218 * positive length and the weights array contains legitimate values.</li>
219 * <li>throws <code>IllegalArgumentException</code> if any of the following are true:
220 * <ul><li>the values array is null</li>
221 * <li>the weights array is null</li>
222 * <li>the weights array does not have the same length as the values array</li>
223 * <li>the weights array contains one or more infinite values</li>
224 * <li>the weights array contains one or more NaN values</li>
225 * <li>the weights array contains negative values</li>
226 * <li>the start and length arguments do not determine a valid array</li></ul>
227 * </li>
228 * <li>returns <code>false</li> if the array is non-null, but
229 * <code>length</code> is 0.
230 * </ul></p>
231 *
232 * @param values the input array
233 * @param weights the weights array
234 * @param begin index of the first array element to include
235 * @param length the number of elements to include
236 * @return true if the parameters are valid and designate a subarray of positive length
237 * @throws MathIllegalArgumentException if the indices are invalid or the array is null
238 * @since 2.1
239 */
240 protected boolean test(
241 final double[] values,
242 final double[] weights,
243 final int begin,
244 final int length) throws MathIllegalArgumentException {
245 return test(values, weights, begin, length, false);
246 }
247
248 /**
249 * This method is used by <code>evaluate(double[], double[], int, int)</code> methods
250 * to verify that the begin and length parameters designate a subarray of positive length
251 * and the weights are all non-negative, non-NaN, finite, and not all zero.
252 * <p>
253 * <ul>
254 * <li>returns <code>true</code> iff the parameters designate a subarray of
255 * non-negative length and the weights array contains legitimate values.</li>
256 * <li>throws <code>MathIllegalArgumentException</code> if any of the following are true:
257 * <ul><li>the values array is null</li>
258 * <li>the weights array is null</li>
259 * <li>the weights array does not have the same length as the values array</li>
260 * <li>the weights array contains one or more infinite values</li>
261 * <li>the weights array contains one or more NaN values</li>
262 * <li>the weights array contains negative values</li>
263 * <li>the start and length arguments do not determine a valid array</li></ul>
264 * </li>
265 * <li>returns <code>false</li> if the array is non-null, but
266 * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>.
267 * </ul></p>
268 *
269 * @param values the input array.
270 * @param weights the weights array.
271 * @param begin index of the first array element to include.
272 * @param length the number of elements to include.
273 * @param allowEmpty if {@code true} than allow zero length arrays to pass.
274 * @return {@code true} if the parameters are valid.
275 * @throws NullArgumentException if either of the arrays are null
276 * @throws MathIllegalArgumentException if the array indices are not valid,
277 * the weights array contains NaN, infinite or negative elements, or there
278 * are no positive weights.
279 * @since 3.0
280 */
281 protected boolean test(final double[] values, final double[] weights,
282 final int begin, final int length, final boolean allowEmpty) throws MathIllegalArgumentException {
283
284 if (weights == null || values == null) {
285 throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
286 }
287
288 if (weights.length != values.length) {
289 throw new DimensionMismatchException(weights.length, values.length);
290 }
291
292 boolean containsPositiveWeight = false;
293 for (int i = begin; i < begin + length; i++) {
294 if (Double.isNaN(weights[i])) {
295 throw new MathIllegalArgumentException(LocalizedFormats.NAN_ELEMENT_AT_INDEX, i);
296 }
297 if (Double.isInfinite(weights[i])) {
298 throw new MathIllegalArgumentException(LocalizedFormats.INFINITE_ARRAY_ELEMENT, weights[i], i);
299 }
300 if (weights[i] < 0) {
301 throw new MathIllegalArgumentException(LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX, i, weights[i]);
302 }
303 if (!containsPositiveWeight && weights[i] > 0.0) {
304 containsPositiveWeight = true;
305 }
306 }
307
308 if (!containsPositiveWeight) {
309 throw new MathIllegalArgumentException(LocalizedFormats.WEIGHT_AT_LEAST_ONE_NON_ZERO);
310 }
311
312 return test(values, begin, length, allowEmpty);
313 }
314 }
315