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