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