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.MathRuntimeException;
020 import org.apache.commons.math.exception.DimensionMismatchException;
021 import org.apache.commons.math.exception.NotPositiveException;
022 import org.apache.commons.math.exception.NullArgumentException;
023 import org.apache.commons.math.exception.util.LocalizedFormats;
024
025 /**
026 * Abstract base class for all implementations of the
027 * {@link UnivariateStatistic} interface.
028 * <p>
029 * Provides a default implementation of <code>evaluate(double[]),</code>
030 * delegating to <code>evaluate(double[], int, int)</code> in the natural way.
031 * </p>
032 * <p>
033 * Also includes a <code>test</code> method that performs generic parameter
034 * validation for the <code>evaluate</code> methods.</p>
035 *
036 * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
037 */
038 public 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.
074 * @param values data array to store
075 * @param begin the index of the first element to include
076 * @param length the number of elements to include
077 * @see #evaluate()
078 */
079 public void setData(final double[] values, final int begin, final int length) {
080 storedData = new double[length];
081 System.arraycopy(values, begin, storedData, 0, length);
082 }
083
084 /**
085 * Returns the result of evaluating the statistic over the stored data.
086 * <p>
087 * The stored array is the one which was set by previous calls to
088 * </p>
089 * @return the value of the statistic applied to the stored data
090 */
091 public double evaluate() {
092 return evaluate(storedData);
093 }
094
095 /**
096 * {@inheritDoc}
097 */
098 public double evaluate(final double[] values) {
099 test(values, 0, 0);
100 return evaluate(values, 0, values.length);
101 }
102
103 /**
104 * {@inheritDoc}
105 */
106 public abstract double evaluate(final double[] values, final int begin, final int length);
107
108 /**
109 * {@inheritDoc}
110 */
111 public abstract UnivariateStatistic copy();
112
113 /**
114 * This method is used by <code>evaluate(double[], int, int)</code> methods
115 * to verify that the input parameters designate a subarray of positive length.
116 * <p>
117 * <ul>
118 * <li>returns <code>true</code> iff the parameters designate a subarray of
119 * positive length</li>
120 * <li>throws <code>IllegalArgumentException</code> if the array is null or
121 * or the indices are invalid</li>
122 * <li>returns <code>false</li> if the array is non-null, but
123 * <code>length</code> is 0.
124 * </ul></p>
125 *
126 * @param values the input array
127 * @param begin index of the first array element to include
128 * @param length the number of elements to include
129 * @return true if the parameters are valid and designate a subarray of positive length
130 * @throws IllegalArgumentException if the indices are invalid or the array is null
131 */
132 protected boolean test(
133 final double[] values,
134 final int begin,
135 final int length) {
136
137 if (values == null) {
138 throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
139 }
140
141 if (begin < 0) {
142 throw new NotPositiveException(LocalizedFormats.START_POSITION, begin);
143 }
144
145 if (length < 0) {
146 throw new NotPositiveException(LocalizedFormats.LENGTH, length);
147 }
148
149 if (begin + length > values.length) {
150 throw MathRuntimeException.createIllegalArgumentException(
151 LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END);
152 }
153
154 if (length == 0) {
155 return false;
156 }
157
158 return true;
159
160 }
161
162 /**
163 * This method is used by <code>evaluate(double[], double[], int, int)</code> methods
164 * to verify that the begin and length parameters designate a subarray of positive length
165 * and the weights are all non-negative, non-NaN, finite, and not all zero.
166 * <p>
167 * <ul>
168 * <li>returns <code>true</code> iff the parameters designate a subarray of
169 * positive length and the weights array contains legitimate values.</li>
170 * <li>throws <code>IllegalArgumentException</code> if any of the following are true:
171 * <ul><li>the values array is null</li>
172 * <li>the weights array is null</li>
173 * <li>the weights array does not have the same length as the values array</li>
174 * <li>the weights array contains one or more infinite values</li>
175 * <li>the weights array contains one or more NaN values</li>
176 * <li>the weights array contains negative values</li>
177 * <li>the start and length arguments do not determine a valid array</li></ul>
178 * </li>
179 * <li>returns <code>false</li> if the array is non-null, but
180 * <code>length</code> is 0.
181 * </ul></p>
182 *
183 * @param values the input array
184 * @param weights the weights array
185 * @param begin index of the first array element to include
186 * @param length the number of elements to include
187 * @return true if the parameters are valid and designate a subarray of positive length
188 * @throws IllegalArgumentException if the indices are invalid or the array is null
189 * @since 2.1
190 */
191 protected boolean test(
192 final double[] values,
193 final double[] weights,
194 final int begin,
195 final int length) {
196
197 if (weights == null) {
198 throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
199 }
200
201 if (weights.length != values.length) {
202 throw new DimensionMismatchException(weights.length, values.length);
203 }
204
205 boolean containsPositiveWeight = false;
206 for (int i = begin; i < begin + length; i++) {
207 if (Double.isNaN(weights[i])) {
208 throw MathRuntimeException.createIllegalArgumentException(
209 LocalizedFormats.NAN_ELEMENT_AT_INDEX, i);
210 }
211 if (Double.isInfinite(weights[i])) {
212 throw MathRuntimeException.createIllegalArgumentException(
213 LocalizedFormats.INFINITE_ARRAY_ELEMENT, weights[i], i);
214 }
215 if (weights[i] < 0) {
216 throw MathRuntimeException.createIllegalArgumentException(
217 LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX, i, weights[i]);
218 }
219 if (!containsPositiveWeight && weights[i] > 0.0) {
220 containsPositiveWeight = true;
221 }
222 }
223
224 if (!containsPositiveWeight) {
225 throw MathRuntimeException.createIllegalArgumentException(
226 LocalizedFormats.WEIGHT_AT_LEAST_ONE_NON_ZERO);
227 }
228
229 return test(values, begin, length);
230 }
231 }
232